<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>ask yoshi taka Blog</title><description>Notes and essays by ask yoshi taka.</description><link>https://veritycost.com/</link><item><title>生成AIがコードを読む時代、私たちはどうコードを理解すべきか？：生きているアプリケーションと死んでいるソースコード</title><link>https://veritycost.com/posts/ai-code-reading-488f1595093/</link><guid isPermaLink="true">https://veritycost.com/posts/ai-code-reading-488f1595093/</guid><description>あるプロダクトやアプリケーションを解析するとき、当然ソースコードを読みます。しかし、ソースコードだけでそのプロダクトが理解できるのでしょうか？ソースコードだけを読んでも、なぜこのアプリがエラーを出すのか、わからなかったことはありませんか？</description><pubDate>Sun, 04 May 2025 12:06:41 GMT</pubDate><content:encoded>&lt;p&gt;あるプロダクトやアプリケーションを解析するとき、当然ソースコードを読みます。しかし、ソースコードだけでそのプロダクトが理解できるのでしょうか？
ソースコードだけを読んでも、なぜこのアプリがエラーを出すのか、わからなかったことはありませんか？&lt;/p&gt;
&lt;h2&gt;ソースコードを使うメリット&lt;/h2&gt;
&lt;p&gt;改めて書くまでもないですが念のため&lt;/p&gt;
&lt;h3&gt;すべてが記載されている&lt;/h3&gt;
&lt;p&gt;ソースコードに基づいてアプリケーションが動く以上当然です&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ソースがドキュメントだ。バグも完全に記述されている。
――まつもとゆきひろ&lt;/p&gt;
&lt;p&gt;『オブジェクト指向スクリプト言語Ruby』（⁠まつもとゆきひろ／石塚圭樹（著⁠）⁠、アスキー）p.563
&lt;a href=&quot;https://gihyo.jp/dev/serial/01/agile/0004&quot;&gt;https://gihyo.jp/dev/serial/01/agile/0004&lt;/a&gt; より孫引き&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;解析手順が豊富&lt;/h3&gt;
&lt;p&gt;プログラミング言語が生まれたときから機械化や調査の対象であり、手法も豊富です。&lt;/p&gt;
&lt;p&gt;e.g. HotSpotの分析&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://google-engtools.blogspot.com/2011/12/bug-prediction-at-google.html&quot;&gt;Bug Prediction at Google&lt;/a&gt; (google-engtools.blogspot.com) - What&lt;/p&gt;
&lt;h3&gt;生成AIが活用しやすい&lt;/h3&gt;
&lt;p&gt;コードなので構文としては似ており、要約や評価は生成AIが得意としているところです&lt;/p&gt;
&lt;h2&gt;ソースコード(だけ)を使うデメリット&lt;/h2&gt;
&lt;p&gt;ある程度生成AIで解決できますが、まだまだ不十分です。&lt;/p&gt;
&lt;h3&gt;量が多すぎて当たりをつけづらい&lt;/h3&gt;
&lt;p&gt;ソースコードは本にも例えられる通り分量が多いです。構造化されていればまだしも、構造化されてないならなおさら当たりをつけづらいですね。&lt;/p&gt;
&lt;p&gt;抽象化を進め量を圧縮したならば、見たい部分を見るまでなんどもジャンプを求められることはよくあります。結局どこで設定しているのかわからない状態。&lt;/p&gt;
&lt;p&gt;ある程度の規模を超えると生成AIでもコンテキスト量制限にかかります。&lt;/p&gt;
&lt;h3&gt;挙動が読みづらい&lt;/h3&gt;
&lt;p&gt;典型的な例として、依存関係の上書きがあります。Aという設定を複数の箇所で設定しているときの順位はコード見てもわからないでしょう。&lt;/p&gt;
&lt;p&gt;さらに環境変数がソースコードの外で設定されていたらどうでしょう。&lt;/p&gt;
&lt;h3&gt;何をしたいのかが不明瞭&lt;/h3&gt;
&lt;p&gt;このソースコードによって最終的に何を解決するのかは不明瞭です。&lt;/p&gt;
&lt;p&gt;結局readmeやドキュメント、誰かの解説頼りになります。&lt;/p&gt;
&lt;p&gt;Howは豊富ですがWhy / So what?が不明瞭ということです。&lt;/p&gt;
&lt;p&gt;コードだけ見てもコードの構造はわかりますがシステムの全体像を把握しづらいですね。&lt;/p&gt;
&lt;h3&gt;優先順位がつけづらい&lt;/h3&gt;
&lt;p&gt;よく使われるhotspotは確率であり、必ずしも重要とは限りません。普段触ってないファイルが重要かもしれません&lt;/p&gt;
&lt;h3&gt;作成したコード外の動きは教えてくれない&lt;/h3&gt;
&lt;p&gt;利用しているライブラリの動き、動作するOSの動きは教えてくれません。親切なときや不具合を修正したときは記録が残っていることがあります。&lt;/p&gt;
&lt;h3&gt;コードが使われているとは限らない&lt;/h3&gt;
&lt;p&gt;パスはあるが現実的には誰も呼んでないことも&lt;/p&gt;
&lt;h2&gt;見るべきは動いているアプリケーション&lt;/h2&gt;
&lt;p&gt;上記を解決…というより補完できるのが動的な解析です。&lt;/p&gt;
&lt;p&gt;どのパスが重要かはAPIの呼び出し量から確認できます。どんな流れで動作しているかはAPMでわかります。パフォーマンスの問題検知も動かしたほうが速いです。コード見るよりログ見たほうが早いことも。&lt;/p&gt;
&lt;p&gt;異常系として停止したときの動きが知りたいならカオスエンジニアリングが便利でしょう。正常系と異常系、両方通じることがそのシステムの専門家への近道です。&lt;/p&gt;
&lt;p&gt;設計図だけ見てもよくわかりませんし、標本やDNAだけ見ても難しい。
(そこからだけで引き出すにはかなりの知識と観察力が必要です。)&lt;/p&gt;
&lt;p&gt;実際の機械や生き物が動いているところを見れば一気に理解が進みます。旭川動物園のような行動展示です。&lt;/p&gt;
&lt;p&gt;百聞は一見にしかずですね。体験しましょう&lt;/p&gt;
&lt;h3&gt;動的な解析と侵襲度&lt;/h3&gt;
&lt;p&gt;非侵襲的、つまり悪い影響を与えないほうが安全ではあります&lt;/p&gt;
&lt;p&gt;メトリクス、トレーシング、ログの順に情報量と同時にリスク(侵襲度)が増えていきます。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/ai-code-reading-488f1595093.png&quot; alt=&quot;目安&quot;&gt;&lt;/p&gt;
&lt;p&gt;特にログは慎重に扱う必要があります。&lt;/p&gt;
&lt;p&gt;ログのリスクはとにかく情報量が多いこととのトレードオフです。ディスクがいっぱいになる、ネットワークがログデータで埋まる、コストが嵩む、各種I/Oを消費する、ローテーション時に重くなる、アプリケーションのリトライなどで一気にログが増加する、ログ収集のリトライで一気に消費が増えるといった事象が起こり得ます。ログが原因でアプリケーションが停止するということです。逆にメトリクスだとそうそう起きません。&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;ソースコードと動的な解析、どちらが先か&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;生成AIでコードを要約しつつ、動的な解析をおすすめします。&lt;/p&gt;
&lt;p&gt;動的にやってみて、その部分のソースコードを読むのを繰り返す。ある程度慣れてきたら通しで読む。&lt;/p&gt;
&lt;p&gt;というのも、詳しくないものの理解を深めるにはまずメンタルモデルを組み立てる必要があるからです。&lt;/p&gt;
&lt;h3&gt;とりあえずやってみる：メンタルモデルの組み立て&lt;/h3&gt;
&lt;p&gt;学習のモデルはいろいろありますが、実務向けのEAT(Experience(経験)、Awareness(気づき)、Theory(理論))にせよ、Accelerated形式にせよ、まずはある程度行動してみることです。&lt;/p&gt;
&lt;p&gt;メンタルモデルを組み立てたあとはモデルを修正していきます。固着したメンタルモデル自体が学習の障害になり得るので、柔軟に壊す。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://commoncog.com/accelerated-expertise/&quot;&gt;Book Summary: Accelerated Expertise&lt;/a&gt; (commoncog.com) - A summary of Accelerated Expertise, the best book on accelerating training programs we have today.&lt;/p&gt;
&lt;h2&gt;理解を進めるうえでの改善&lt;/h2&gt;
&lt;p&gt;目的を持つことで理解が深めやすくなります。&lt;/p&gt;
&lt;p&gt;ソースコードも漫然と読まず、疑問を持ちながら読んだり、改善点を見つけながら読みましょう。ソースコードの改善はもちろん、ドキュメントの改善、テストの追加…&lt;/p&gt;
&lt;p&gt;バリデーションなどは簡単なシミュレータを作ることで便利になります。&lt;/p&gt;
&lt;p&gt;動的な理解もそうです。どこが遅いのか、重要なAPIはどれか、リスクが大きそうな部分はどこか…自問自答しましょう。&lt;/p&gt;
&lt;h2&gt;最後に&lt;/h2&gt;
&lt;p&gt;生成AIでだいぶ楽になったところはあっても、新規プロジェクトを除けば開発のうちほとんどがコード理解と言われますし、実際にそう感じます。&lt;/p&gt;
&lt;p&gt;長い時間アプリに関わっていても理解が浅い人はだいたいObservabilityに興味がありません。理解が早くて深い人になるために、実際に動いているアプリケーションを見て触り観測しましょう。&lt;/p&gt;
&lt;h2&gt;関連記事&lt;/h2&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>2025年現在、AI開発支援が組織に与える現実的なインパクト</title><link>https://veritycost.com/posts/ai-dev-support-impact-2025-fea488178e1c/</link><guid isPermaLink="true">https://veritycost.com/posts/ai-dev-support-impact-2025-fea488178e1c/</guid><description>日本でもすでに動きは起こっているので整理します。</description><pubDate>Mon, 14 Apr 2025 17:57:30 GMT</pubDate><content:encoded>&lt;p&gt;日本でもすでに動きは起こっているので整理します。&lt;/p&gt;
&lt;h2&gt;開発人材・外注ニーズの変化と凋落&lt;/h2&gt;
&lt;p&gt;簡単なことはAIに任せれば良い、ジュニアレベルのエンジニアは不要になるとよく言われています。&lt;/p&gt;
&lt;p&gt;ここで、どのような業務が任せられるかもう少し分解して具体的に考えてみましょう。&lt;/p&gt;
&lt;p&gt;以下のような部分を委託することは以前からよく行われてきました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;機能拡張で、既存の似たような部分を真似して作る&lt;/li&gt;
&lt;li&gt;簡単なものを明確に切り出して、タスクやチケットとして渡す&lt;/li&gt;
&lt;li&gt;あまり重要ではないツールの作成をまるっとお任せする&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これらはすべて、現在のAIが得意する領域であり、すでに実務で取り入れられ、高速で解決されています。&lt;/p&gt;
&lt;p&gt;したがって、以下の採用は大きく抑制されます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;週2,3日程度の副業(開発)&lt;/li&gt;
&lt;li&gt;オフショア&lt;/li&gt;
&lt;li&gt;ジュニアエンジニア/インターン (よく言われていることですね)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;社内ミニツールによる効率化&lt;/h3&gt;
&lt;p&gt;使い捨てのスクリプト、社内用の管理ツールが容易に作成できます。&lt;/p&gt;
&lt;p&gt;開発者は定義的にも他者のために効率化をしますが、意外と自分たちの効率化ツールを作らないものです。(feature factoryの罠)&lt;/p&gt;
&lt;p&gt;生成AIそのものだけではなく、生成AIで作ったミニツールにより、人手で行っていたものが効率化されます。そして人手で行っているようなものはあまり難度が高くない傾向にあることから、ニーズが変わります&lt;/p&gt;
&lt;h2&gt;リリースがすべて: より速く、より小さくのために投資できるか&lt;/h2&gt;
&lt;p&gt;AIにより開発できる機能の分量が増えれば増えるほど、リリースプロセス全体がボトルネックになります。&lt;/p&gt;
&lt;p&gt;もしリリース頻度を増やせず、複数のものをまとめてリリースすることになれば災害が起こります。アイテムごとの障害率を考えれば、まとめることで障害確率は当然増えますし、組み合わさった新しい障害が生じることもあります。原因特定が難しくなり時間がかかりることもあれば、ロールバックが難しくもなります。&lt;/p&gt;
&lt;p&gt;残念ながらほぼすべての日本企業はリリースが貧弱であり、基本中の基本であるロールバックすら期待される水準に達していないことが多いようです。&lt;/p&gt;
&lt;p&gt;リリースをより速く、より頻度を増やし、より安全にするには以下のようものを手に入れる努力が必要です&lt;/p&gt;
&lt;h3&gt;開発面&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;デプロイとリリースの分離、フラグによるコントロール、セグメンテーションとステージドロールアウト、それらごとのモニタリング、自動ロールバック、GitHubフローに類するブランチモデル、CI/CDの高速化…&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;上記の概念を聞いたことがないなら、それだけ差がついているということでしょう。これらを導入するには、工数も時間も技術力も優先順位変更も必要です。&lt;/p&gt;
&lt;p&gt;一方、VercelやCloudflareを基盤としているごく最近のスタートアップなら、基盤に組み込まれているので容易でしょう。&lt;/p&gt;
&lt;h3&gt;QA面&lt;/h3&gt;
&lt;p&gt;E2E含めた自動テストの拡張が必須です。外部の会社にQA作業を委託している企業が多いようですが、手動テストでは間に合わなくなるでしょう。&lt;/p&gt;
&lt;h3&gt;開発関係以外の側面&lt;/h3&gt;
&lt;p&gt;顧客へのリリースとコミュニケーションプロセス改善、文面作成プロセスの効率化、開発以外の部分の単純化、慣性の打破、なにより意思&lt;/p&gt;
&lt;p&gt;リリース改善はプロセス改善であり、人間に関わる難しい問題です。ここで実力に差が生まれます。&lt;/p&gt;
&lt;h2&gt;準開発者への支援&lt;/h2&gt;
&lt;p&gt;組織の中には、開発者ほどではなくとも時折プログラミングがタスクになる人がいます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;コーポレートIT/情報システム&lt;/li&gt;
&lt;li&gt;Webマーケティング (タグやMAなど)&lt;/li&gt;
&lt;li&gt;技術サポート&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;このような人たちはプログラミングが専門というわけではないですが素養がある傾向にあります。そして社内受託に近く、極小の人数で評価されないわりに多くの要求に苦しんでいます。&lt;/p&gt;
&lt;p&gt;こんなときAI支援によりプログラムやツールをサクサク作れれば効率化が見込めます。開発とその他の狭間でボールが落ちがちなので支援できると良いですね。&lt;/p&gt;
&lt;h2&gt;最後に&lt;/h2&gt;
&lt;p&gt;個人がAIを使うのは簡単ですが、組織的な変化によって初めて大きな成果が生まれます。気づいて動きましょう&lt;/p&gt;
&lt;h2&gt;関連記事&lt;/h2&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>言語モデル/AIによるITエンジニアリング論点</title><link>https://veritycost.com/posts/ai-engineering-impacts-bc83a7007db4/</link><guid isPermaLink="true">https://veritycost.com/posts/ai-engineering-impacts-bc83a7007db4/</guid><description>AIによってITエンジニアの業務がどのように変わるのでしょうか。「仕事がなくなる！単価が一気に下がる！」といった意見もありますが、裏付けがやや大雑把な印象です。もう少し具体的な影響を短期、中期、長期で検討してみます。</description><pubDate>Sat, 15 Apr 2023 08:53:31 GMT</pubDate><content:encoded>&lt;p&gt;AIによってITエンジニアの業務がどのように変わるのでしょうか。
「仕事がなくなる！単価が一気に下がる！」といった意見もありますが、裏付けがやや大雑把な印象です。もう少し具体的な影響を短期、中期、長期で検討してみます。&lt;/p&gt;
&lt;p&gt;＊プロンプトインジェクション含め、セキュリティ関係は除いて考えます。セキュリティだけでかなりの分量になるため。&lt;/p&gt;
&lt;h2&gt;短期目線&lt;/h2&gt;
&lt;h3&gt;採用：単純なコーディング、技術試験は不要に&lt;/h3&gt;
&lt;p&gt;まず、AIを使えば問題をすぐ解ける以上、試験として成り立ってないため避けるべきです。&lt;/p&gt;
&lt;p&gt;そして、そもそも機械のほうが得意なことを測定する意味がなくなりました。採石場から石材を運ぶ人員を採用するために、個人の腕力や脚力を測定するようなもの。今必要なのは大型ダンプを扱う技能です。&lt;/p&gt;
&lt;p&gt;＊AI以前に選考方式としては効果もエビデンスも弱く無意味という指摘もあります。&lt;/p&gt;
&lt;h3&gt;乗り換えられるようにする&lt;/h3&gt;
&lt;p&gt;AIを使ったサービスが大量に生まれ始めました。オープンソースのツールも多数あります。自社で基盤を作成することもあるでしょう。&lt;/p&gt;
&lt;p&gt;今は1ヶ月単位で進歩があるほど進化が激しい状態です。何を使うにせよ、乗り換え可能なのが重要です。すぐ捨てられるようにしましょう。手のひらを返す速度で勝ちましょう&lt;/p&gt;
&lt;h2&gt;中期目線&lt;/h2&gt;
&lt;h3&gt;可読性のあるコードは不要になるのでは&lt;/h3&gt;
&lt;p&gt;毎回AIがコードを生成し、AIがドキュメントを書き、AIがコードの要点と影響範囲、変更点を抽出するなら、ソースコードに人間が介在する余地がありません。さよならリーダブルコード…&lt;/p&gt;
&lt;h3&gt;保守性、再利用性も不要になるのでは&lt;/h3&gt;
&lt;p&gt;毎回ソースコードを生成すれば保守性や再利用性も不要です。&lt;/p&gt;
&lt;h3&gt;コードのリファクタリングもいらない&lt;/h3&gt;
&lt;p&gt;可読性や保守性の観点では不要に&lt;/p&gt;
&lt;h3&gt;プログラミング言語選択の基準が変わる&lt;/h3&gt;
&lt;p&gt;可読性や書きやすさは考慮不要となりそうです。一方、実績が豊富と有利です。過去データが大量に溜まっているため。&lt;/p&gt;
&lt;h3&gt;新しいプログラミング言語は不要になるか&lt;/h3&gt;
&lt;p&gt;要求がなければ無理に新しい言語を作る動機はなくなります。新しい言語には当然過去コードが存在しないのでAIが使えなくなります。&lt;/p&gt;
&lt;p&gt;一方、新しい時代、AIに最適化した、AIネイティブの言語が出てきてもおかしくありません。学習用コードもある程度の量が生まれれば十分です。&lt;/p&gt;
&lt;h3&gt;(Web)フレームワークは不要になるか&lt;/h3&gt;
&lt;p&gt;Webフレームワークを織り込んだ学習モデルが前提になることから、しばらくは必須でしょう。&lt;/p&gt;
&lt;p&gt;Webフレームワークの目的である開発効率周りをほぼAIで達成できるとすると不要になりかねません。&lt;/p&gt;
&lt;h3&gt;最新の構文、書き方、イディオムに追随できるか&lt;/h3&gt;
&lt;p&gt;あくまで過去パターンなので、新しい書き方を誰かが書いて訓練する必要があります。&lt;/p&gt;
&lt;h3&gt;パフォーマンスのオートチューニングが可能に&lt;/h3&gt;
&lt;p&gt;すでにプロンプトをもとに簡単なタスク分解、タスク実行までできる状態です。&lt;/p&gt;
&lt;p&gt;目標値を指定すれば、自動的に負荷試験環境を作成し、負荷試験実行、評価、変更のフィードバックサイクルを回すまでが視野に入ります。&lt;/p&gt;
&lt;p&gt;RDBの設定レベルでは数年前から存在するのですが(&lt;a href=&quot;https://aws.amazon.com/blogs/machine-learning/tuning-your-dbms-automatically-with-machine-learning/&quot;&gt;資料&lt;/a&gt;を&lt;a href=&quot;https://ottertune.com/&quot;&gt;参照&lt;/a&gt;)、コーディングレベル、アプリケーションレベルで可能になります。&lt;/p&gt;
&lt;h3&gt;形式手法による一部機能のバグゼロ達成&lt;/h3&gt;
&lt;p&gt;TLA+やAlloyといった形式手法は著名事例を除きほぼ開発に使われていませんが、AIが「仕様」を書くようになれば、一気に標準になる可能性があります。それにより形式手法を適用しやすい部分に関して、欠陥がないことを証明できるようになります。&lt;/p&gt;
&lt;h3&gt;品質の向上(事前欠陥検出)&lt;/h3&gt;
&lt;p&gt;UTのテストケースがテスト技法にそってないことがよくありますが、AIが書いたりレビューすることでケース自体の品質向上が期待できます。&lt;/p&gt;
&lt;p&gt;テストのメンテナンスコストも話題になりますが、AIが生成すればメンテナンスコストを考えなくてすむため、大量のテストケースを実行し続けることが可能です。&lt;/p&gt;
&lt;p&gt;Mutation TestやProperty-based testingといったあまり使われてない手法も容易に利用できそうです。&lt;/p&gt;
&lt;p&gt;パフォーマンスと同様、これもフィードバックループによるオートチューニングが期待できます。&lt;/p&gt;
&lt;h3&gt;オートチューニングのための実験できる環境が必要&lt;/h3&gt;
&lt;p&gt;オートチューニングの話が何回か出てきました。本番ワークロードをもとにフィードバックループを回すためには、本番環境で安全に実験できる仕組みが不可欠です。&lt;/p&gt;
&lt;p&gt;カナリアやリリースコントロールが必須になるでしょう。&lt;br&gt;＊実際はSaaSビジネスなら現時点で必須です。&lt;/p&gt;
&lt;h3&gt;ローカル開発環境は必要か&lt;/h3&gt;
&lt;p&gt;AIを使うにはサーバと通信する必要があります。光の速度が有限である以上、ローカル環境からサーバに毎回通信すると時間がかかりストレスが溜まります。&lt;/p&gt;
&lt;p&gt;GitHub Codespacesのような、リモート開発による脱ローカル環境が浸透しつつありますが、AIの面からも背中を押す可能性があります。&lt;/p&gt;
&lt;h3&gt;いわゆる開発のしやすさはいらなくなるか&lt;/h3&gt;
&lt;p&gt;機械が読めれば良いとすると、Developer Experienceは不要になるかもしれません。LLM Experience(仮)を向上させる必要があります。&lt;/p&gt;
&lt;p&gt;AIさんがスムーズに開発できるよう、いろいろしてあげるイメージです。&lt;/p&gt;
&lt;h3&gt;IaC(&lt;strong&gt;Infrastructure as Code&lt;/strong&gt;)は必要なのか&lt;/h3&gt;
&lt;p&gt;インフラをコードにするメリットはプロンプトで代用できます。プロンプトを通して指示すれば十分になりそうです。&lt;/p&gt;
&lt;p&gt;IaCの仕組み自体は残るかもしれませんが、人間がなにかする必要はなくなりそうです。&lt;/p&gt;
&lt;h3&gt;CI/CDはさらに重要になりそう&lt;/h3&gt;
&lt;p&gt;フィードバックループを回すための基盤です。
テレメトリや各種データを集約した、AIが使うプラットフォームとなりそうです。&lt;/p&gt;
&lt;h3&gt;(データ分析での)SQLの重要性が減る&lt;/h3&gt;
&lt;p&gt;SQLで数値を問い合わせるより、プロンプトからAIに説明してもらったほうが的確でしょう。さようなら&lt;a href=&quot;http://mickindex.sakura.ne.jp/database/celko/celko_tis.html&quot;&gt;Having句&lt;/a&gt;。&lt;/p&gt;
&lt;h3&gt;より少人数の開発に。フルスタック化が進む&lt;/h3&gt;
&lt;p&gt;今までのシステム開発の流れを鑑みると、生産性が上がるにつれ少人数化、多能工化が進んできました。今が1チームでPdM:エンジニアが1:4から1:10ぐらいだとすると、1:3ぐらいになるかもしれません。&lt;/p&gt;
&lt;h3&gt;EM(Engineering Manager)のポジションが減る&lt;/h3&gt;
&lt;p&gt;組織の中でエンジニアの割合が高いことから、EMが生まれてきた面があると思います。エンジニアの割合が減少すればEMのポジションが減りそうです。&lt;/p&gt;
&lt;h3&gt;組織内での分散化が進む&lt;/h3&gt;
&lt;p&gt;重複開発を避け、共通部分を作成しリソース最適化として効率化するか、重複開発を許容してでも開発の依存関係をなくしフロー最適化するか。&lt;/p&gt;
&lt;p&gt;永遠のジレンマです。&lt;/p&gt;
&lt;p&gt;生産性が高まるに連れ、リソースよりフローを重視し、分散化に天秤が傾くでしょう。スピードのほうが大事になります。&lt;/p&gt;
&lt;h3&gt;社内プラットフォームの価値が低下するか？&lt;/h3&gt;
&lt;p&gt;重複開発大いに結構とすると、あえてプラットフォームを保有する価値が減ります。
一方、プラットフォームも爆速かつ低コストで作れるようになるとすれば、価値は変わらないとも考えられます。&lt;/p&gt;
&lt;h3&gt;日本のレガシーIT企業が壊滅する&lt;/h3&gt;
&lt;p&gt;日本では古い技術、古いアプリケーションを大事に長く使う傾向があります。特にIT企業であっても創業から10年20年経つとそうなります。&lt;/p&gt;
&lt;p&gt;今まではそれでも問題ありませんでした。世界中の攻撃者は日本語が読めず、日本をあえて狙う組織はアジア拠点がほとんどでした。&lt;/p&gt;
&lt;p&gt;AIにより日本語の壁がなくなり、今まで以上に攻撃が低コストになり、無差別で攻撃されるようになると、レガシープロダクトがすべて乗っ取られ、既存IT企業が壊滅する可能性があります。
日本は犯罪者の狩場になりそうです。&lt;/p&gt;
&lt;p&gt;＊セキュリティの話ですが日本特有と思われるので書いておきます。&lt;/p&gt;
&lt;h2&gt;長期目線&lt;/h2&gt;
&lt;h3&gt;むしろソースコードは不要で機械語でよいのでは？&lt;/h3&gt;
&lt;p&gt;これは飛躍しすぎですね。AIといってもソースコードのオートコンプリートなので。とはいえ誰かが学習モデルを作れば可能かもしれません&lt;/p&gt;
&lt;h3&gt;タイピング力は必要か&lt;/h3&gt;
&lt;p&gt;いよいよConversational Programmingが主流になるということで、画面の前で話すだけでプログラムが作成できるようになります。スマホでもOK。&lt;/p&gt;
&lt;p&gt;キーボードといえばHHKBやREALFORCEではなくYAMAHAの話が主流になりそうです（？）&lt;/p&gt;
&lt;h3&gt;ITエンジニアとAI、どちらが勝つか&lt;/h3&gt;
&lt;p&gt;もちろん勝者は資本家です。歴史的にもよくあることでした。ホワイトカラーが壊滅するとなると、農家も勝つかもしれません。コモディティ強い。現物持ちましょう。大混乱に備え食料も備蓄しておきましょう&lt;/p&gt;
&lt;h2&gt;さいごに&lt;/h2&gt;
&lt;p&gt;IT開発関係としては10年に一度の激動の時代になりました。特に今回の波は、今までの波を乗り越えてきたBigTechも終わりになりえる時代だと思います。2023年4月現在、AIの進化が速いので月1でよいのでLLM関係の情報を追いたいですね。&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>AIがサクサク動くコードベースにしよう</title><link>https://veritycost.com/posts/ai-sakusaku-codebase-9436af887e4f/</link><guid isPermaLink="true">https://veritycost.com/posts/ai-sakusaku-codebase-9436af887e4f/</guid><description>コードベースの状態によってはAIを使っても生産性が上がりにくいことがあります。どのようなコードベースだとAIが力を発揮しやすいでしょうか。以下を参考にしてみてください。</description><pubDate>Mon, 02 Jun 2025 01:24:09 GMT</pubDate><content:encoded>&lt;p&gt;コードベースの状態によってはAIを使っても生産性が上がりにくいことがあります。どのようなコードベースだとAIが力を発揮しやすいでしょうか。以下を参考にしてみてください。&lt;/p&gt;
&lt;h2&gt;コードは短く&lt;/h2&gt;
&lt;p&gt;コードが短いほうがコンテキストに収まりやすいです&lt;/p&gt;
&lt;h2&gt;ファイルを責務ごとに分割し、凝集度を高くする&lt;/h2&gt;
&lt;p&gt;必要な情報がコンテキストに収まりやすくなります。影響箇所が少ないほうがコンテキスト読み出しが不要になり安定します。&lt;/p&gt;
&lt;p&gt;特にAIエージェントの場合、最低限のコンテキストだけを読む事が多いので大事です。無関係な読み出しで時間をつかったり迷走したりするのは避けたいところ&lt;/p&gt;
&lt;h2&gt;不要な設定やメッセージ用テキストは別ファイルに分離する&lt;/h2&gt;
&lt;p&gt;普段は読まなくてすむのでコンテキストが節約できます。&lt;/p&gt;
&lt;h2&gt;コメントは無駄なく丁寧に&lt;/h2&gt;
&lt;p&gt;コメントがあるほうがAIが理解しやすくなります。特にWhyはコードベースからは読み取りづらいのでWhyを書きましょう&lt;/p&gt;
&lt;p&gt;かといって見ればわかることはコンテキストの無駄なので避けましょう&lt;/p&gt;
&lt;h2&gt;変数名は丁寧に&lt;/h2&gt;
&lt;p&gt;変数名が適切ならAIが理解しやすくなります。&lt;/p&gt;
&lt;h2&gt;ディレクトリ構造に秩序を&lt;/h2&gt;
&lt;p&gt;どこに何があるか、時間をかけて探さなくて済むようにしましょう&lt;/p&gt;
&lt;h2&gt;主流のライブラリ/ツール等を使う&lt;/h2&gt;
&lt;p&gt;主流のほうが学習サンプルが多いため精度が高くなります&lt;/p&gt;
&lt;h2&gt;ドキュメントがしっかりしたライブラリ/ツールを使う&lt;/h2&gt;
&lt;p&gt;ドキュメントが細かいほうが読ませたときに精度が高くなります&lt;/p&gt;
&lt;h2&gt;テストを厚めに書く&lt;/h2&gt;
&lt;p&gt;テストが多いほうが修正ミスを捉えやすくなります
また、テストから仕様がわかるためスムーズに理解できるようになります。&lt;/p&gt;
&lt;h2&gt;開発ドキュメントを書く&lt;/h2&gt;
&lt;p&gt;ドキュメントを読ませることでお互いの前提が揃い、同じ間違いが減ります。やり方や場所をまとめておきましょう。AIは記憶喪失が起きるので毎回教えてあげる必要があり、それが簡単なほうが便利です。&lt;/p&gt;
&lt;h2&gt;ビルドやテスト時間は短く&lt;/h2&gt;
&lt;p&gt;イテレーション速度が上がり、単位時間あたりのアウトプットが増えます。
AIの思考や記述時間よりこちらがネックになることがあります。&lt;/p&gt;
&lt;p&gt;これらの部分もAIで高速化しましょう&lt;/p&gt;
&lt;h2&gt;診断/デバッグ方法を準備しておく&lt;/h2&gt;
&lt;p&gt;複雑な不具合はデバッグログや機能のon/offなどで診断できるようにしましょう。AIエージェントが実行できるぐらい平易だとよいですね&lt;/p&gt;
&lt;p&gt;このような部分もAIで作りましょう&lt;/p&gt;
&lt;h2&gt;さいごに&lt;/h2&gt;
&lt;p&gt;整理整頓されたコードベースとそうでないコードベースでは、AIが出せる生産性に差が生まれます。&lt;/p&gt;
&lt;p&gt;AIの生産性を上げるために、AIでコードベースを洗練させましょう。&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>CTOに役立つ！AWS請求からアーキテクチャを読み、コストを削減する技術。PDF編</title><link>https://veritycost.com/posts/aws-billing-pdf-architecture-cost-reduction-tips-d3cf0a1ad308/</link><guid isPermaLink="true">https://veritycost.com/posts/aws-billing-pdf-architecture-cost-reduction-tips-d3cf0a1ad308/</guid><description>とにかくAWSのアーキテクチャやコストを今すぐ把握しないといけない！ということはたまにあります。知っている人の話を聞くことが最も重要なことの一つですが、文書からわかることも多いでしょう。</description><pubDate>Sun, 30 Apr 2023 07:34:40 GMT</pubDate><content:encoded>&lt;p&gt;とにかくAWSのアーキテクチャやコストを今すぐ把握しないといけない！ということはたまにあります。知っている人の話を聞くことが最も重要なことの一つですが、文書からわかることも多いでしょう。&lt;/p&gt;
&lt;p&gt;この記事では請求書のPDFという、あまり利用されないが強力なツールから読み解く方法を解説します。&lt;/p&gt;
&lt;p&gt;手っ取り早く概況を掴みたい新任CTOや、M&amp;#x26;Aなんて言われて困っているCTO、EM、Staff+ Engineerにおすすめです。&lt;/p&gt;
&lt;h2&gt;請求書PDFが読めるようになるメリット&lt;/h2&gt;
&lt;h3&gt;ある程度アーテクチャがわかる&lt;/h3&gt;
&lt;p&gt;構成図やドキュメントは更新されていなかったり、存在しないことすらあります。&lt;/p&gt;
&lt;p&gt;請求書に乗るキャッシュフローは事実。逆引きして読み解くスキルさえあれば、構成図より正確です。&lt;/p&gt;
&lt;p&gt;「利益は意見、キャッシュは事実」に近いですね。&lt;/p&gt;
&lt;h3&gt;同じ条件で比較して、コスト削減の余地がつかめる&lt;/h3&gt;
&lt;p&gt;どのアカウントも請求書は同一条件で提出されます。決算書の開示に近いですね。管理会計は別の仕組みにしましょう。&lt;/p&gt;
&lt;h3&gt;DDやPMIにも効果的&lt;/h3&gt;
&lt;p&gt;買収(検討)時のDD(Due Diligence)ではビジネス部分が最優先。IT DDとして情報システム周りの検討はありえますが、プロダクト開発運用部分の優先順位は非常に低く、短い時間や少ない機会のなか、資料をもらうことすら困難です。&lt;/p&gt;
&lt;p&gt;PDF一つで概略がわかるようになれば、改善の余地が探れ、シナジーが予測できるようになります。何より自社より水準が低い場合、PMIの覚悟ができます。&lt;/p&gt;
&lt;h2&gt;請求書からわかること&lt;/h2&gt;
&lt;p&gt;PDFだけでも思ったよりたくさんのことがわかります&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Savings Planの利用状況 (コスト削減の余地)&lt;/li&gt;
&lt;li&gt;AWS契約の状況&lt;/li&gt;
&lt;li&gt;開発/本番でAWSアカウントを分離しているかどうか&lt;/li&gt;
&lt;li&gt;プロダクトやサービスごとにアカウントが分離できているか&lt;/li&gt;
&lt;li&gt;同一のorganizationに入れているか&lt;/li&gt;
&lt;li&gt;どのAWSサービスを利用しているか&lt;/li&gt;
&lt;li&gt;大まかなアーキテクチャ&lt;/li&gt;
&lt;li&gt;セキュリティの成熟度&lt;/li&gt;
&lt;li&gt;おおよその技術力&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;請求書(invoice)であり、Billing(明細)やCost Explorerは除きます。Billingが確認できると解像度が一気に上がり、最適な提案が可能になります。Cost Explorerを使えるとamortizationも見えるので理想です。&lt;/p&gt;
&lt;h2&gt;Amazon Chime&lt;/h2&gt;
&lt;p&gt;音声/ビデオ通話関係の処理がある可能性があります。電気通信事業法を含め、業界関係の法律やガイドラインを確認する必要があります&lt;/p&gt;
&lt;h2&gt;Amazon Connect&lt;/h2&gt;
&lt;p&gt;支払会社の関係で領収書のPDFは別です。&lt;/p&gt;
&lt;p&gt;電話関係の処理がある可能性があります。業界関係の法律やガイドラインを確認する必要があります。特にアウトバウンドに注意。&lt;/p&gt;
&lt;h2&gt;Amplify&lt;/h2&gt;
&lt;p&gt;ある程度モダンなアーキテクチャが想定されます。ビルド料金が含まれるので確認。&lt;/p&gt;
&lt;h2&gt;API Gateway&lt;/h2&gt;
&lt;p&gt;API Gatewayでバランシングしてるかがわかります。バランシング候補の一つ。&lt;/p&gt;
&lt;h3&gt;合わせて確認&lt;/h3&gt;
&lt;p&gt;ELB / AppSync&lt;/p&gt;
&lt;h2&gt;AppStream&lt;/h2&gt;
&lt;p&gt;Windowsが潜んでいる可能性があります。テック企業で必要とされるようなエンジニアとはスキルセットが違うので難しいです。&lt;/p&gt;
&lt;h2&gt;AppSync&lt;/h2&gt;
&lt;p&gt;ある程度モダンなアーキテクチャが想定されます。&lt;/p&gt;
&lt;h3&gt;合わせて確認&lt;/h3&gt;
&lt;p&gt;ELB / API Gateway&lt;/p&gt;
&lt;h2&gt;CloudFront&lt;/h2&gt;
&lt;p&gt;CloudFrontの利用がないがS3はあるなら、CDNが使えていない可能性があります。&lt;/p&gt;
&lt;p&gt;おおよそのアクセス数がわかります。ビジネスに比べて多すぎるようなら、botや攻撃、権限設定ミスの可能性があります&lt;/p&gt;
&lt;h3&gt;合わせて確認&lt;/h3&gt;
&lt;p&gt;S3&lt;/p&gt;
&lt;h2&gt;CloudSearch&lt;/h2&gt;
&lt;p&gt;実質deprecatedなので移行を考える必要があります。&lt;/p&gt;
&lt;h2&gt;CloudTrail&lt;/h2&gt;
&lt;p&gt;2017年8月から開設したアカウントはデフォルトonです。料金が0ならそれより古く、またセキュリティ成熟度が低いでしょう。&lt;/p&gt;
&lt;h2&gt;CloudWatch&lt;/h2&gt;
&lt;p&gt;ログ(CloudWatch Logs)が一番多いことが想定されます。ビジネスやアクセス量次第です。3rd Partyのモニタリングツールを入れているなら、ポーリングによるMetricsの料金も嵩みます。&lt;/p&gt;
&lt;p&gt;EC2利用アプリケーションではCloudWatch LogsではなくローカルのEBSに出力している可能性があります。NAT Gateway含め通信コストも確認。&lt;/p&gt;
&lt;p&gt;Metricsのコストを、不要リージョンを封鎖する方法もあります。特に日本のみがターゲットのビジネスでは有効です。&lt;/p&gt;
&lt;h2&gt;Code*&lt;/h2&gt;
&lt;p&gt;CI/CDをAWSサービスに寄せているということです。自社との統合を検討しましょう。&lt;/p&gt;
&lt;h2&gt;Cognito&lt;/h2&gt;
&lt;p&gt;料金があるならMAUを逆算できます。連携なしログインだと5万MAUまで無料であり、toBだとほとんど費用がかかってないことも。&lt;/p&gt;
&lt;h2&gt;Config&lt;/h2&gt;
&lt;p&gt;あらゆるプラクティスでonにしろと指南されており、存在しないとセキュリティに不安が残ります。&lt;/p&gt;
&lt;p&gt;料金がかかっていても下げる方法に気づいていないことが多いです。
特にEC2のAuto Scalingと組み合わさると嵩みます。&lt;/p&gt;
&lt;p&gt;高頻度デプロイやSecurity Hubが原因のことも。
高頻度デプロイはむしろ望ましいことです。&lt;/p&gt;
&lt;h3&gt;合わせて確認&lt;/h3&gt;
&lt;p&gt;EC2&lt;/p&gt;
&lt;h2&gt;Cost Explorer&lt;/h2&gt;
&lt;p&gt;Cost Explorerを熱心に見ている人がいるということです。期待が持てます。&lt;/p&gt;
&lt;h2&gt;Data Pipeline&lt;/h2&gt;
&lt;p&gt;Data Pipelineはメンテナンスモードに入りました。移行の必要があります。&lt;/p&gt;
&lt;h2&gt;📝 Data Transfer&lt;/h2&gt;
&lt;p&gt;ビジネスに対してアウトバウンド通信量が多すぎないか、確認したいです。CDNが使えてないか、最悪攻撃の可能性があります。&lt;/p&gt;
&lt;h3&gt;合わせて確認&lt;/h3&gt;
&lt;p&gt;CloudFront&lt;/p&gt;
&lt;h2&gt;Database Migration Service&lt;/h2&gt;
&lt;p&gt;システム移行中かもしれません。一応通常時のレプリケーションにも使えますが、最適ではなさそうです。&lt;/p&gt;
&lt;h2&gt;Direct Connect&lt;/h2&gt;
&lt;p&gt;オンプレミスと接続している可能性があります。&lt;/p&gt;
&lt;h2&gt;DynamoDB&lt;/h2&gt;
&lt;p&gt;主なデータストアかどうかを金額で判断します&lt;/p&gt;
&lt;h2&gt;📝 EC2&lt;/h2&gt;
&lt;p&gt;EC2と聞くとEC2インスタンス周りのように見えますが、歴史的経緯から関係ないものも混ざっています。代表的なものがNAT GatewayとEBSです。Cost Explorerでは「EC2 その他」の扱いです。&lt;/p&gt;
&lt;p&gt;やたら金額が大きいときは、アーキテクチャが古く、開発や組織がうまく回ってない可能性があります。一方、外部製品のセルフホスト版を運用していることもあります。&lt;/p&gt;
&lt;p&gt;EC2があっても、RDSなどのデータストアが料金にないことがあります。RDBが自前運用の可能性があるのでマネージドサービスに移したいところです。&lt;/p&gt;
&lt;p&gt;ある程度近代的なアーキテクチャだが金額が多いとすると、NAT GatewayのData Processed Byteが多い可能性があります。S3 / DynamoDBの利用状況によってはVPC Endpointを推奨。
アプリの初期化や起動時に、ライブラリやイメージを取得していることもあります。特にFargateならイメージPullにコストがかかりがちです。&lt;/p&gt;
&lt;h3&gt;合わせて確認&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;RDS&lt;/li&gt;
&lt;li&gt;ELB / API Gateway&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;ELB&lt;/h2&gt;
&lt;p&gt;ELBでバランシングしてるかがわかります。バランシング候補の一つ。&lt;/p&gt;
&lt;h3&gt;合わせて確認&lt;/h3&gt;
&lt;p&gt;ELB / AppSync&lt;/p&gt;
&lt;h2&gt;GuardDuty&lt;/h2&gt;
&lt;p&gt;存在するならある程度セキュリティ意識があることが読み取れます。&lt;/p&gt;
&lt;h2&gt;KMS&lt;/h2&gt;
&lt;p&gt;高すぎるときは、クレデンシャルを取得するKMSアクセスがアプリケーション内でキャッシュができてない可能性があります。&lt;/p&gt;
&lt;h3&gt;合わせて確認&lt;/h3&gt;
&lt;p&gt;Secrets Manager&lt;/p&gt;
&lt;h2&gt;📝 RDS&lt;/h2&gt;
&lt;p&gt;データストアとして一番可能性があります。存在しない場合、なんのマネージドサービスをデータストアとしているか、EC2で運用していないかを確認。&lt;/p&gt;
&lt;h2&gt;Secrets Manager&lt;/h2&gt;
&lt;p&gt;高すぎるときは、クレデンシャルを取得するSecrets Managerアクセスがアプリケーション内でキャッシュができてない可能性があります。&lt;/p&gt;
&lt;h3&gt;合わせて確認&lt;/h3&gt;
&lt;p&gt;KMS&lt;/p&gt;
&lt;h2&gt;Security Hub&lt;/h2&gt;
&lt;p&gt;Onということは最低限セキュリティに気を使っています。&lt;/p&gt;
&lt;p&gt;Config代が嵩む原因のこともあります。&lt;/p&gt;
&lt;h2&gt;📝 S3&lt;/h2&gt;
&lt;p&gt;S3料金が多いと、一見データ量が原因のように見えますが、思ったよりAPIアクセスが多い可能性があります。特にLIST API。&lt;/p&gt;
&lt;p&gt;S3の利用がないのにEC2は多い場合、静的ファイルをサーバから流す構成になっている可能性があります。&lt;/p&gt;
&lt;h3&gt;合わせて確認&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;CloudFront&lt;/li&gt;
&lt;li&gt;Data Transfer&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;サポート&lt;/h3&gt;
&lt;p&gt;重要なプロダクトについて、本番環境でサポート契約がないと不安です。&lt;/p&gt;
&lt;h2&gt;WAF&lt;/h2&gt;
&lt;p&gt;セキュリティにある程度気を使っている可能性があります&lt;/p&gt;
&lt;h2&gt;Marketplace&lt;/h2&gt;
&lt;p&gt;支払会社の関係で領収書のPDFは別です。&lt;/p&gt;
&lt;p&gt;古めのサービスを表しているかもしれませんし、3rd Party SaaSをAWS経由で購入したほうが楽という重い組織構造を表しているかもしれません。&lt;/p&gt;
&lt;h2&gt;PDFだけだとわからないことの例&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;RIの購入状況&lt;/li&gt;
&lt;li&gt;外部ベンダーから付与される割引クーポンの規模&lt;/li&gt;
&lt;li&gt;各種インスタンスの古さ&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;BillingやCost Explorerなら読み取れます。&lt;/p&gt;
&lt;h2&gt;結論&lt;/h2&gt;
&lt;p&gt;請求書PDFをざっと眺めるだけでも結構なことがわかりますね。&lt;/p&gt;
&lt;p&gt;BillingとCost Explorerが追加できればほとんど明らかにできます。Billingから読み解く記事が続編になる予定です。&lt;/p&gt;
&lt;p&gt;コスト削減のご相談は&lt;a href=&quot;https://twitter.com/ask_yoshi_taka&quot;&gt;@ask_yoshi_taka&lt;/a&gt;まで。請求書PDFだけでも提案できます&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>AWS Config Advanced Query: SQL-Like Queries for Fast Cloud Insights</title><link>https://veritycost.com/en/posts/aws-config-advanced-query-sql-like-queries-for-fast-cloud-insights-aa0e2715a615/</link><guid isPermaLink="true">https://veritycost.com/en/posts/aws-config-advanced-query-sql-like-queries-for-fast-cloud-insights-aa0e2715a615/</guid><description>Instantly analyze AWS resource configurations across accounts without complex scripts</description><pubDate>Mon, 10 Feb 2025 13:36:17 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://veritycost.com/en/assets/aws-config-advanced-query-sql-like-queries-for-fast-cloud-insights-aa0e2715a615.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Instantly analyze AWS resource configurations across accounts without complex scripts&lt;/h2&gt;
&lt;p&gt;Ever struggled to understand the actual configuration of your AWS resources?&lt;/p&gt;
&lt;p&gt;IaC (Infrastructure as Code) is great, but every team abstracts things differently, making it hard to know what’s actually configured in AWS right now.&lt;/p&gt;
&lt;p&gt;Need to quickly check configuration differences between dev and production?&lt;/p&gt;
&lt;p&gt;Looking for a way to query AWS resources across multiple accounts without writing custom scripts?&lt;/p&gt;
&lt;p&gt;There’s an underrated but powerful AWS-native tool that simplifies this: AWS Config Advanced Query.&lt;/p&gt;
&lt;p&gt;With SQL-like queries, you can instantly pull resource configurations across your AWS accounts, ensuring compliance, debugging misconfigurations, and tracking infrastructure changes — all without complex scripts or manual checks.&lt;/p&gt;
&lt;p&gt;In this guide, we’ll explore practical examples to help you leverage AWS Config Advanced Query for better cloud visibility and governance.&lt;/p&gt;
&lt;h2&gt;Find Lambda functions using a runtime older than Node.js 22&lt;/h2&gt;
&lt;p&gt;This query identifies all Lambda functions that are still using Node.js versions older than 22.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT
  accountID,
  resourceId,
  configuration.runtime,
  configuration.lastModified,
  configuration.description
WHERE
  resourceType = &apos;AWS::Lambda::Function&apos;
  AND configuration.runtime LIKE &apos;nodejs%&apos;
  AND configuration.runtime &amp;#x3C; &apos;nodejs22.x&apos;
  ORDER BY accountID
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Don’t just detect EOL — celebrate the team that upgrades to the latest version the fastest! 🏎️ 🏁&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Find instances configured with Aurora I/O-Optimized&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;This query lists all RDS instances that have the Aurora I/O-Optimized storage type enabled.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT
    accountId,
    configuration.dBInstanceClass,
    configuration.engineVersion,
    resourceName
WHERE
    resourceType = &apos;AWS::RDS::DBInstance&apos;
    AND configuration.storageType = &apos;aurora-iopt1&apos;
ORDER BY
    accountId
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I/O-Optimized comes with a 30% premium on Reserved Instance pricing.
Fun fact: It also improves I/O speed!&lt;/p&gt;
&lt;h2&gt;Find tables using Provisioned Mode in DynamoDB&lt;/h2&gt;
&lt;p&gt;This query finds all DynamoDB tables using Provisioned Mode instead of On-Demand Mode.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT
  resourceId,
  accountId,
  configuration.provisionedThroughput.readCapacityUnits,
  configuration.provisionedThroughput.writeCapacityUnits
WHERE
  resourceType = &apos;AWS::DynamoDB::Table&apos;
  AND (
    configuration.provisionedThroughput.readCapacityUnits &gt; 0
    OR configuration.provisionedThroughput.writeCapacityUnits &gt; 0
  )
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unless you’re a DynamoDB expert, On-Demand mode is recommended over Provisioned mode.&lt;/p&gt;
&lt;h2&gt;Find CloudFront Distributions that do not support IPv6&lt;/h2&gt;
&lt;p&gt;This query identifies all CloudFront distributions that have IPv6 disabled.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT
  accountId,
  configuration.aliasICPRecordals,
  configuration.distributionConfig.cacheBehaviors.items,
  configuration.distributionConfig.httpVersion,
WHERE
  resourceType = &apos;AWS::CloudFront::Distribution&apos;
  AND configuration.distributionConfig.isIPV6Enabled = false
order by
  accountId
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;strong&gt;List all used Public IPs across accounts&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;This query extracts all used Public IP addresses across AWS accounts.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT
  accountId,
  configuration.association.publicIp,
  configuration.interfaceType,
  availabilityZone,
  resourceId,
WHERE
  resourceType = &apos;AWS::EC2::NetworkInterface&apos;
  AND configuration.association.publicIp &gt; &apos;0.0.0.0&apos;
ORDER BY
  accountId,
  configuration.interfaceType
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;List all Elastic IPs (EIPs)&lt;/h2&gt;
&lt;p&gt;This query lists all allocated Elastic IPs (EIPs) across AWS accounts.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT
  accountId,
  resourceName,
  awsRegion,
  resourceId,
  relationships,
WHERE
  resourceType=&apos;AWS::EC2::EIP&apos;
ORDER BY
  accountId
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;AWS Config resource schema documentation is as follows:&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;AWS Config Advanced Query makes it easy to analyze resource configurations across accounts with SQL-like queries. No complex scripts or manual checks are needed — just write a query and get insights instantly.&lt;/p&gt;
&lt;p&gt;Start using AWS Config Advanced Query today to improve cloud governance, detect misconfigurations, and simplify compliance monitoring!&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>AWSコストチェインの頻出パターンを抑え一気にコストを削減する</title><link>https://veritycost.com/posts/aws-cost-chain-breakers-11f78cd39306-ja/</link><guid isPermaLink="true">https://veritycost.com/posts/aws-cost-chain-breakers-11f78cd39306-ja/</guid><description>AWSの複数サービスが連鎖するコスト構造を理解し、起点側から抑制するパターンを紹介します。</description><pubDate>Thu, 15 Aug 2024 14:26:47 GMT</pubDate><content:encoded>&lt;p&gt;AWSのコスト削減のために単一のサービスとにらめっこしていても効果が薄いでしょう。複数のサービスが連鎖するコスト構造を理解することで、チェインの起点から修正しレバレッジを効かせて削減することができます。&lt;/p&gt;
&lt;p&gt;この記事では、起点側の修正で終点側の呼び出しを抑えるパターンを取り上げます。&lt;br&gt;
＊終点側を高速化することで起点も高速化するパターンは除きます。&lt;/p&gt;
&lt;p&gt;前提として、大まかなコスト内訳は確認済みとします。&lt;/p&gt;
&lt;h2&gt;S3 -&gt; CloudTrail -&gt; GuardDuty&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/aws-cost-chain-breakers-11f78cd39306-ja-01.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;S3オブジェクトへのリクエストはCloudTrailにデータイベントという形で記録され、それをGuardDutyが監査する、といったパターンの連鎖です。&lt;/p&gt;
&lt;h3&gt;クライアントから抑える&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/aws-cost-chain-breakers-11f78cd39306-ja-02.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;外部からのアクセスが嵩むならCloudFrontを挟む
(CloudFrontはCloudTrailに記録されない。アクセスログはS3同様取得可能)&lt;/li&gt;
&lt;li&gt;特にAPIからのS3呼び出しを控え、署名付きURLを活用する&lt;/li&gt;
&lt;li&gt;オブジェクト一覧が必要ならS3 Inventoryを検討する&lt;/li&gt;
&lt;li&gt;データ基盤宛の場合はテーブル形式からジョブのアクセス方式まで見直す&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;CloudTrailへの記録を抑える&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/aws-cost-chain-breakers-11f78cd39306-ja-03.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;CloudTrailで記録するデータイベントはフィルタリング可能です。AWSアカウントのワークロードと既存のセキュリティ施策、セキュリティ要件に応じて検討しましょう。除くことでこの経路でトレースができなくなりますが支障ないかどうかです。&lt;/p&gt;
&lt;h2&gt;AWS Config -&gt; SecurityHub -&gt; GuardDuty&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/aws-cost-chain-breakers-11f78cd39306-ja-04.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;ECS TasksやEC2が起動終了を繰り返すことでネットワークインターフェースなどにアクセスし、それがAWS Configに記録され、さらにSecurity Hubなどで検査される連鎖パターンです&lt;/p&gt;
&lt;p&gt;Amazon Inspector起因のパターンもありました&lt;/p&gt;
&lt;h3&gt;初期トリガーから抑える&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/aws-cost-chain-breakers-11f78cd39306-ja-05.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;もしECS Taskの数や起動頻度を下げることができれば抑えることができます。率直に言って難しく、アーキテクチャレベルの検討が必要になることが多いでしょう。&lt;/p&gt;
&lt;h3&gt;AWS Configの記録量を抑える&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/aws-cost-chain-breakers-11f78cd39306-ja-06.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;この連鎖ではこちらが本命です。やり方は二種類あります&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;特定の記録種類を除外する&lt;/li&gt;
&lt;li&gt;随時記録から日次記録に変更する
全体変更のほか特定の記録種類だけに留めることも可能&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ワークロードと既存のセキュリティ施策、セキュリティ要件を考慮して選択しましょう。&lt;/p&gt;
&lt;h2&gt;API Gateway + Lambda&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/aws-cost-chain-breakers-11f78cd39306-ja-07.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;上記は基本的な図であり、Lambdaの内部では別のチームのAPIを追加で呼び出し、DBでのデータ追加変更はデータ基盤に流れ、ログはログ基盤に保存されるといった連鎖が続くことになります。&lt;/p&gt;
&lt;p&gt;API Gateway + Lambdaとしていますが、ELB+ECSなどでもおおよそ同じです。&lt;/p&gt;
&lt;h3&gt;クライアントで抑える&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/aws-cost-chain-breakers-11f78cd39306-ja-08.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;クライアントとバックエンドでの担当チームが異なるとやや難しいですが、起点だけあり一番効果が大きいです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ポーリングを控える。頻度を下げる&lt;/li&gt;
&lt;li&gt;不要なリクエストを排除する&lt;/li&gt;
&lt;li&gt;クライアント側でキャッシュしてもらう&lt;/li&gt;
&lt;li&gt;リトライを抑える（ためにエラーリクエストを減らすことも可能。一部はバックエンドで対応可能)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;特に基幹系アプリケーションでポーリングさせるとユニットあたり原価が跳ね、粗利率に対して大きい影響がありえます&lt;/p&gt;
&lt;h3&gt;API Gateway周辺で抑える&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/aws-cost-chain-breakers-11f78cd39306-ja-09.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;ELBでもだいたい同じです&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;preflightリクエストのOptionsメソッドを固定レスポンスにする&lt;/li&gt;
&lt;li&gt;preflightリクエストにAccess-Control-Max-Ageをつける&lt;/li&gt;
&lt;li&gt;API Gatewayのキャッシュ機構を使う&lt;/li&gt;
&lt;li&gt;Lambda Authorizerのttlキャッシュを使う&lt;/li&gt;
&lt;li&gt;処理不要でレスポンスの中身を問わないなら固定レスポンス機能を使う&lt;/li&gt;
&lt;li&gt;Lambdaを挟まず直接AWSサービスを呼び出すことも検討する&lt;/li&gt;
&lt;li&gt;マッピングテンプレート(VTL)だけで処理が終わらないか検討する&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Lambdaで抑える&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/aws-cost-chain-breakers-11f78cd39306-ja-10.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;DBアクセスを控える、クエリチューニングするのは当然として、&lt;/p&gt;
&lt;p&gt;ローカルメモリキャッシュを使う方法があります。ただしインスタンスレベルなので別のインスタンスには効果がないほか、インスタンス終了時に消えてしまいます。&lt;/p&gt;
&lt;p&gt;初期起動の段階で全キャッシュする方法もあります。起動時にDBから取得して保存する、sqlite/levelDBなどを内蔵しておくなど。キャッシュの宿命で同期の方法は必ず検討しましょう(stale cache)&lt;/p&gt;
&lt;p&gt;当然多数のインスタンスがあればすべてのインスタンスで保持されるわけなので無駄があるといえばあります。DBから全取得の場合、thundering herdが起きやすいのでトラブル時のカスケード障害に注意が必要です。ほとんど存在しない項目の存在チェックを毎回しているならブルームフィルタも効果的です。&lt;/p&gt;
&lt;p&gt;リモートキャッシュという形でキャッシュサーバを準備する話になると、アーキテクチャレベルで修正が必要になるため割愛します。&lt;/p&gt;
&lt;p&gt;次はログの部分です。ロギングの実装方法次第ですが、JSONを自前で組み立てると無駄な処理時間を使うのでロガーに任せましょう。テンプレート機能が活用できます。&lt;/p&gt;
&lt;p&gt;このときログを少しでも切り詰めるとログ代が抑えられます。スペースを限界まで削る、キー名に略語を使い、uuidは切り詰める(そもそもuuidの必要性を確認する)&lt;/p&gt;
&lt;p&gt;本番アプリケーションでスタックトレースをそのまま出すのはやめましょう。開発やライブラリ制作者用であり、本番でのトラブルシューティングにはほぼ使えません。きちんとコード内でハンドリングして行動に結びつくメッセージを出力するとよいです。&lt;/p&gt;
&lt;p&gt;ログと一口に言ってもいろいろあり、監査に使うような「アーカイブ」ならCloudWatch Logsに直接出力する必要はありません。(保存エラー時の処理を検討しつつ、) S3に直接保存するほうがコストが抑えられます。&lt;/p&gt;
&lt;h2&gt;最後に&lt;/h2&gt;
&lt;p&gt;CloudTrailやGuardDutyあたりは後段のため、これらだけが増えることはなく起因があるはずです。&lt;/p&gt;
&lt;p&gt;構造と流れ、数字とその変化を理解すればアセスメントと提案までは容易です。コストを本気で削減したいならDeepDiveしましょう。&lt;/p&gt;
&lt;p&gt;クラウドコスト半減&amp;#x26;原価作り込みに関心がある方は歓迎します。&lt;/p&gt;
&lt;h2&gt;コスト削減したい人向け記事集&lt;/h2&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>Breaking the Cost Chain: A Deep Dive into Reducing AWS Costs via Service Dependency Optimization</title><link>https://veritycost.com/en/posts/aws-cost-chain-breakers-f741ac914cd9/</link><guid isPermaLink="true">https://veritycost.com/en/posts/aws-cost-chain-breakers-f741ac914cd9/</guid><description>💰 Struggling to optimize AWS costs despite tweaking individual services?</description><pubDate>Mon, 03 Feb 2025 13:37:26 GMT</pubDate><content:encoded>&lt;h2&gt;💰 &lt;strong&gt;Struggling to optimize AWS costs despite tweaking individual services?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Many engineers fine-tune AWS resources — adjusting Lambda memory settings, optimizing EC2 instance types, or enabling S3 lifecycle policies — expecting noticeable cost savings. But despite these AWS cost optimization efforts, total AWS expenses often remain stubbornly high.&lt;/p&gt;
&lt;p&gt;The real issue? Hidden cost chains. A single S3 request can trigger CloudTrail logging, which then feeds into GuardDuty analysis, creating a cascading effect that inflates your AWS bill.&lt;/p&gt;
&lt;p&gt;This guide provides actionable AWS cost optimization strategies for DevOps engineers, SREs, and FinOps professionals looking to reduce AWS costs at scale by breaking down interdependent cost chains. We’ll explore high-impact optimizations that go beyond isolated fixes, helping you minimize AWS expenses without sacrificing performance or security.&lt;/p&gt;
&lt;p&gt;🚀 Let’s dive into AWS cost chain optimization best practices and start cutting unnecessary cloud expenses today.&lt;/p&gt;
&lt;h2&gt;AWS Config -&gt; SecurityHub -&gt; GuardDuty&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/en/assets/breaking-the-cost-chain-a-deep-dive-into-reducing-aws-costs-via-service-dependency-optimization-f741ac914cd9-01.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Frequent start/stop cycles of ECS tasks or EC2 instances generate network interface events. These events are recorded by AWS Config and subsequently analyzed by Security Hub, forming another cost chain that adds to your overall expenses.&lt;/p&gt;
&lt;p&gt;When running off-the-shelf solutions like Databricks on AWS, frequent EC2 instance lifecycle events are common, further increasing the volume of logged network interface changes.&lt;/p&gt;
&lt;p&gt;In some cases, Amazon Inspector may also contribute to this cost chain, depending on your security configurations.&lt;/p&gt;
&lt;p&gt;💰 &lt;strong&gt;Quick Wins&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/config/latest/developerguide/select-resources-excluding.html&quot;&gt;Exclude specific log types.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/config/latest/developerguide/managing-recorder_console-change-recording-frequency.html&quot;&gt;Switch from continuous logging to daily logging.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Optimizing from the First Trigger: ECS Task Launches&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/en/assets/breaking-the-cost-chain-a-deep-dive-into-reducing-aws-costs-via-service-dependency-optimization-f741ac914cd9-02.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Reducing the number or frequency of ECS Task launches can help lower costs. However, achieving this often requires architectural changes, which can be complex and challenging to implement.&lt;/p&gt;
&lt;h3&gt;Minimizing AWS Config Logs&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/en/assets/breaking-the-cost-chain-a-deep-dive-into-reducing-aws-costs-via-service-dependency-optimization-f741ac914cd9-03.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The key optimization here is reducing AWS Config logging overhead. There are two main approaches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/config/latest/developerguide/select-resources-excluding.html&quot;&gt;Exclude specific log types.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/config/latest/developerguide/managing-recorder_console-change-recording-frequency.html&quot;&gt;Switch from continuous logging to daily logging.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Choose the best approach based on your workload, existing security measures, and security requirements to ensure an optimal balance between cost savings and compliance.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;S3 -&gt; CloudTrail -&gt; GuardDuty&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/en/assets/breaking-the-cost-chain-a-deep-dive-into-reducing-aws-costs-via-service-dependency-optimization-f741ac914cd9-04.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Requests to S3 objects are logged as data events in CloudTrail. These events are then audited by GuardDuty, creating a cost chain that compounds expenses across services.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Optimizing from the First Trigger: S3 Access&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/en/assets/breaking-the-cost-chain-a-deep-dive-into-reducing-aws-costs-via-service-dependency-optimization-f741ac914cd9-05.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Implement &lt;strong&gt;CloudFront&lt;/strong&gt; if external access is high. Unlike direct S3 requests, CloudFront requests are not logged in CloudTrail, but access logs can still be obtained.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;signed URLs&lt;/strong&gt; to minimize direct API calls to S3.&lt;/li&gt;
&lt;li&gt;Consider&lt;strong&gt;S3 Inventory&lt;/strong&gt;for frequent object listings as a cost-effective alternative.&lt;/li&gt;
&lt;li&gt;Review table formats and access patterns in data pipelines to optimize performance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition, executing a large number of Athena queries can generate a high volume of “StartQuery” events in CloudTrail.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Reducing CloudTrail Logging Overhead&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/en/assets/breaking-the-cost-chain-a-deep-dive-into-reducing-aws-costs-via-service-dependency-optimization-f741ac914cd9-06.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;CloudTrail allows you to filter data events, reducing unnecessary logging overhead. Evaluate the impact of excluding specific logs based on your workload, existing security measures, and compliance requirements.&lt;/p&gt;
&lt;p&gt;However, be cautious — removing logs may affect traceability and limit forensic capabilities.&lt;/p&gt;
&lt;h2&gt;API Gateway + Lambda&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/en/assets/breaking-the-cost-chain-a-deep-dive-into-reducing-aws-costs-via-service-dependency-optimization-f741ac914cd9-07.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The diagram above represents a basic architecture. However, within the Lambda function, additional API calls to other teams’ services may occur, database updates propagate to the data platform, and logs are stored in the logging infrastructure — forming an extended chain of dependencies.&lt;/p&gt;
&lt;p&gt;Although this example uses API Gateway + Lambda, a similar architecture can be implemented with ELB + ECS with comparable results.&lt;/p&gt;
&lt;p&gt;💰 &lt;strong&gt;Quick Wins&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Optimize preflight requests: Use a &lt;strong&gt;fixed response for OPTIONS&lt;/strong&gt; method requests.&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;Access-Control-Max-Age&lt;/strong&gt; for preflight requests: Reduce the frequency of repeated CORS preflight requests.&lt;/li&gt;
&lt;li&gt;Use fixed responses when processing is unnecessary: If the response content does not matter, use API Gateway’s fixed response feature.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;Optimizing at the Client Side&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/en/assets/breaking-the-cost-chain-a-deep-dive-into-reducing-aws-costs-via-service-dependency-optimization-f741ac914cd9-08.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Reducing unnecessary requests at the client side is one of the most impactful optimizations since it directly reduces the load on the backend. While this can be challenging when different teams manage the client and backend separately, addressing inefficiencies at the source yields the greatest cost savings.&lt;/p&gt;
&lt;p&gt;Key Strategies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Minimize polling: Reduce the frequency of polling or avoid it whenever possible.&lt;/li&gt;
&lt;li&gt;Eliminate unnecessary requests: Prevent redundant API calls that do not add value.&lt;/li&gt;
&lt;li&gt;Leverage client-side caching: Encourage caching to minimize duplicate requests.&lt;/li&gt;
&lt;li&gt;Reduce retries: Decrease error-prone requests to limit automatic retries (some cases can be handled on the backend).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In enterprise applications, excessive polling can significantly increase per-unit costs, potentially impacting gross margins.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Optimizing Around API Gateway&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/en/assets/breaking-the-cost-chain-a-deep-dive-into-reducing-aws-costs-via-service-dependency-optimization-f741ac914cd9-09.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The same optimizations generally apply to ELB as well. Reducing overhead at the API Gateway level can improve performance and lower costs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Strategies:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Optimize preflight requests: Use a &lt;strong&gt;fixed response for OPTIONS&lt;/strong&gt; method requests.&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;Access-Control-Max-Age&lt;/strong&gt; for preflight requests: Reduce the frequency of repeated CORS preflight requests.&lt;/li&gt;
&lt;li&gt;Leverage API Gateway caching: Store frequent responses to minimize redundant processing.&lt;/li&gt;
&lt;li&gt;Enable &lt;strong&gt;TTL caching for Lambda Authorizer&lt;/strong&gt;: Reduce the number of authorization calls.&lt;/li&gt;
&lt;li&gt;Use fixed responses when processing is unnecessary: If the response content does not matter, use API Gateway’s fixed response feature.&lt;/li&gt;
&lt;li&gt;Consider calling AWS services directly instead of using Lambda: Reduce unnecessary function invocations.&lt;/li&gt;
&lt;li&gt;Evaluate whether processing can be handled with mapping templates (VTL) alone: Avoid Lambda where possible by utilizing API Gateway’s built-in processing capabilities.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;Minimizing Overhead in Lambda&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/en/assets/breaking-the-cost-chain-a-deep-dive-into-reducing-aws-costs-via-service-dependency-optimization-f741ac914cd9-10.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Optimizing Database Access and Caching&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;While optimizing queries and minimizing database access is fundamental, additional caching strategies can further reduce overhead.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Leverage local memory caching: This can improve performance, but it is instance-specific, meaning it does not persist across instances and is lost when the instance is terminated.&lt;/li&gt;
&lt;li&gt;Preload data at startup: Fetch necessary data from the database at startup and store it locally, or embed a lightweight database like SQLite or LevelDB. However, caching inherently introduces stale cache risks, so synchronization strategies must be carefully considered.&lt;/li&gt;
&lt;li&gt;Be mindful of redundancy: If multiple instances maintain separate caches, memory usage may become inefficient. Additionally, bulk retrieval from the database at startup can trigger a thundering herd problem, potentially leading to cascading failures.&lt;/li&gt;
&lt;li&gt;Use Bloom filters for rare existence checks: If your system frequently checks for the existence of rarely used items, Bloom filters can be an effective solution.&lt;/li&gt;
&lt;li&gt;Remote caching requires architectural changes: Using a dedicated cache server (e.g. Redis) involves architectural modifications and is beyond the scope of this discussion.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Optimizing Logging for Cost Efficiency&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The way logging is implemented significantly impacts performance and cost.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Avoid unnecessary processing when structuring logs: Instead of manually constructing JSON logs, rely on logging libraries with built-in template functions to minimize overhead.&lt;/li&gt;
&lt;li&gt;Reduce log size to lower costs: Optimize storage by eliminating unnecessary spaces, using abbreviations for key names, and truncating UUIDs (after verifying if full UUIDs are truly necessary).&lt;/li&gt;
&lt;li&gt;Do not expose full stack traces in production: Stack traces are primarily useful for development and library maintainers but are rarely effective for production troubleshooting. Instead, ensure proper error handling and output actionable log messages that guide resolution steps.&lt;/li&gt;
&lt;li&gt;Optimize long-term audit logs: If logs are intended for long-term retention and auditing, storing them directly in S3 instead of CloudWatch Logs can significantly cut costs. However, ensure proper handling for storage failures.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;CloudTrail and GuardDuty operate downstream in the service chain, meaning their cost increases are always triggered by upstream events. Identifying the root cause is key to effective cost optimization.&lt;/p&gt;
&lt;p&gt;By understanding the architecture, data flow, and numerical trends, conducting assessments and making recommendations becomes much easier. If you’re serious about cost reduction, it’s time to Deep Dive.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Recommended Articles for Cost Optimization&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;This article is the English version of the original article written in Japanese, available &lt;a href=&quot;https://veritycost.com/posts/aws-cost-chain-breakers-11f78cd39306-ja/&quot;&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>Practical AWS FinOps: Cost Management from Unit Economics to Optimization</title><link>https://veritycost.com/en/posts/aws-cost-management-b8733381/</link><guid isPermaLink="true">https://veritycost.com/en/posts/aws-cost-management-b8733381/</guid><description>A comprehensive guide to AWS cost management and FinOps — covering data democratization, unit economics, showback, daily operations, and cost reduction strategies.</description><pubDate>Fri, 10 Jul 2026 01:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;1. First Steps: Democratization&lt;/h2&gt;
&lt;h3&gt;Democratize Cost Data&lt;/h3&gt;
&lt;p&gt;Ideally, tools like AWS Cost Explorer should be visible to all AWS users. There is no reason to hide this data. Without visibility, engineers lack the motivation to reduce costs and cannot determine priorities.&lt;/p&gt;
&lt;h3&gt;Leverage AWS Organizations&lt;/h3&gt;
&lt;p&gt;It is highly recommended to use AWS Organizations to gain cross-account visibility. Logging into each account individually to check costs is inefficient and painful.&lt;/p&gt;
&lt;h3&gt;IAM Permissions&lt;/h3&gt;
&lt;p&gt;Keep in mind that access control and IAM policies for these views require careful planning (e.g., saving custom views requires update permissions).&lt;/p&gt;
&lt;h3&gt;Key Features to Enable&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Cost Explorer (and saved views)&lt;/li&gt;
&lt;li&gt;Savings Plans / Reserved Instances (RI) dashboards&lt;/li&gt;
&lt;li&gt;Cost Optimization Hub&lt;/li&gt;
&lt;li&gt;Cost Anomaly Detection&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2. Clarify Unit Economics&lt;/h2&gt;
&lt;h3&gt;Tie Costs to Business Metrics&lt;/h3&gt;
&lt;p&gt;It is natural for AWS costs to increase as revenue or product usage grows. However, if you do nothing, you won’t know whether a cost spike is driven by business growth or pure inefficiency.&lt;/p&gt;
&lt;h3&gt;Define Your “Unit”&lt;/h3&gt;
&lt;p&gt;The right unit depends on your business model:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Brick-and-mortar / Retail Tech:&lt;/strong&gt; Number of locations/stores.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;B2C SaaS:&lt;/strong&gt; Number of active users.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Developer Tools:&lt;/strong&gt; Number of user accounts.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Align with FP&amp;#x26;A&lt;/h3&gt;
&lt;p&gt;Collaborate closely with the Financial Planning &amp;#x26; Analysis (FP&amp;#x26;A) team to identify your core cost drivers. Discuss unit costs rather than just the total aggregate spend.&lt;/p&gt;
&lt;h3&gt;Isolate Controllable Metrics&lt;/h3&gt;
&lt;p&gt;Separate what the development team can control from what they cannot. Engineers cannot directly control total revenue, but they can easily optimize the cost per unit. Breaking down responsibility unlocks more actionable solutions.&lt;/p&gt;
&lt;h3&gt;Level Up Your Metrics&lt;/h3&gt;
&lt;p&gt;To mature your FinOps practice, break down costs beyond the product level into specific feature levels. This is relatively easier to achieve if you are using Serverless architectures.&lt;/p&gt;
&lt;h3&gt;Separate Dev vs. Prod Costs&lt;/h3&gt;
&lt;p&gt;Development environments are largely fixed costs proportional to the number of engineers. Production environments are variable costs proportional to customer volume. Your primary target for strict management should be Production costs.&lt;/p&gt;
&lt;h2&gt;3. Amortization &amp;#x26; Showback&lt;/h2&gt;
&lt;h3&gt;Centralized Discount Management&lt;/h3&gt;
&lt;p&gt;Upfront discounts from Savings Plans or Volume Discounts (PPA) are usually owned by a centralized team. However, you must accurately reflect these benefits to individual product teams.&lt;/p&gt;
&lt;h3&gt;Default to Amortized Costs&lt;/h3&gt;
&lt;p&gt;If you do not look at amortized data, individual product costs will appear to spike or drop erratically. Make it a habit to use Amortized Costs as the default view in AWS Cost Explorer.&lt;/p&gt;
&lt;h3&gt;Separate Cash Flow from Cost Control&lt;/h3&gt;
&lt;p&gt;Treat the cash flow of purchasing Savings Plans as a separate issue from daily cost optimization. This aligns with the principle of isolating controllable metrics—dev teams cannot control enterprise-level discount purchases.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;4. Monthly / Quarterly Tasks&lt;/h2&gt;
&lt;h3&gt;Prioritize Savings Plans (SP) over Reserved Instances (RI)&lt;/h3&gt;
&lt;p&gt;Savings Plans offer higher flexibility, reducing the risk of missed discounts or over-commitment while lowering management overhead.&lt;/p&gt;
&lt;h3&gt;When to Buy SP&lt;/h3&gt;
&lt;p&gt;Unless you have highly predictable architectures and a dedicated team member specializing in RI management, stick to Savings Plans.&lt;/p&gt;
&lt;h3&gt;Leverage Instance Savings Plans&lt;/h3&gt;
&lt;p&gt;Note that SageMaker, Lambda, and Fargate are covered by Compute SP, while Instance SP offers higher discounts for specific EC2 families. Also, utilize DB Savings Plans, which cover not only RDS but also OpenSearch and DynamoDB.&lt;/p&gt;
&lt;h3&gt;Strategic Over-commitment&lt;/h3&gt;
&lt;p&gt;Targeting a slight over-commitment can sometimes yield better ROI than under-committing.&lt;/p&gt;
&lt;h3&gt;Staggered Purchases&lt;/h3&gt;
&lt;p&gt;Divide your purchases into multiple cycles throughout the year to allow easier adjustments.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;5. Daily Tasks&lt;/h2&gt;
&lt;h3&gt;AWS Cost Anomaly Detection&lt;/h3&gt;
&lt;p&gt;This is a highly effective, free anomaly detection tool. Even though it has a 1-to-2-day delay from the actual event, catching alerts here is far better than realizing a spike on next month’s invoice.&lt;/p&gt;
&lt;h3&gt;Alert Setup&lt;/h3&gt;
&lt;p&gt;Set up one master alert for the AWS management team, and encourage individual product teams to set up their own.&lt;/p&gt;
&lt;h3&gt;Threshold Tuning&lt;/h3&gt;
&lt;p&gt;Fine-tuning the alert thresholds is essential to avoid alert fatigue.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;6. Strategies for Cost Reduction&lt;/h2&gt;
&lt;h3&gt;Understand the Bill Details&lt;/h3&gt;
&lt;h4&gt;Analyze at the Resource Level&lt;/h4&gt;
&lt;p&gt;Use Cost Explorer’s hourly/resource granularity to analyze costs down to specific Lambda functions or CloudWatch Logs groups.&lt;/p&gt;
&lt;h4&gt;Look Beyond the Service Name&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;An “EC2” charge often includes NAT Gateway data transfer fees.&lt;/li&gt;
&lt;li&gt;An “S3” charge might be driven by API request calls rather than actual data storage.&lt;/li&gt;
&lt;li&gt;Sometimes, an abnormal spike in a service’s cost is caused by an external DDoS attack.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Verify Facts, Don’t Guess&lt;/h4&gt;
&lt;p&gt;Check the AWS Billing page and Cost Explorer to see exactly which AWS APIs are draining cash. Rely on hard data, not assumptions.&lt;/p&gt;
&lt;h3&gt;Grasp the Request Cost Structure&lt;/h3&gt;
&lt;h4&gt;Visualize a Single Request&lt;/h4&gt;
&lt;p&gt;Map out a basic API request flow to see how much each component costs.&lt;/p&gt;
&lt;h4&gt;Include Hidden Fees&lt;/h4&gt;
&lt;p&gt;Factor in NAT Gateways, outbound data transfers, and cross-AZ communication fees. Add the high-cost items directly to your architecture diagrams.&lt;/p&gt;
&lt;h4&gt;Account for Third-Party Expenses&lt;/h4&gt;
&lt;p&gt;Don’t forget external APIs, partner integrations, and operational tooling. Third-party tracing and logging platforms (e.g., Datadog, New Relic) tend to be deceptively expensive.&lt;/p&gt;
&lt;h4&gt;The Power of Upstream Caching&lt;/h4&gt;
&lt;p&gt;Visualizing the data flow shows why reducing requests as close to the user as possible is so valuable. Edge or frontend caching is highly cost-effective.&lt;/p&gt;
&lt;p&gt;Data flow is cash flow—imagine money leaking out with every step a request takes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bonus&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;These architecture maps are also incredibly useful for data security (e.g., tracking PII flow) and performance optimization.&lt;/p&gt;
&lt;h3&gt;Performance ≈ User Experience ≈ Cost&lt;/h3&gt;
&lt;h4&gt;Align Cost with UX&lt;/h4&gt;
&lt;p&gt;“Cost cutting” alone often fails to motivate engineering teams. Instead, prioritize optimizations that simultaneously reduce latency and improve user experience. Lowering API latency or utilizing frontend caching reduces data transfer costs while making the app faster for users.&lt;/p&gt;
&lt;h4&gt;Focus on the Hot Paths&lt;/h4&gt;
&lt;p&gt;Unlike pure performance tuning, cost-driven optimization should focus strictly on the high-traffic, standard execution paths.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bonus&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You can also pitch this as a sustainability/decarbonization initiative.&lt;/p&gt;
&lt;h3&gt;Scalability and Reliability Perspectives&lt;/h3&gt;
&lt;h4&gt;Waste Impacts Stability&lt;/h4&gt;
&lt;p&gt;Wasted resources can hinder autoscaling and lead to system failures. Like performance, frame cost optimization as a way to improve reliability to elevate its priority.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;7. Execution: Framework for Optimization&lt;/h2&gt;
&lt;h3&gt;Focus Rigorously on Production Apps&lt;/h3&gt;
&lt;p&gt;For a growing business, focus on reducing production environment costs that impact unit economics (the parts that scale with user growth). Dev environment costs are usually a small fraction of the budget and are driven by headcount. Spending too much time cutting dev costs is counterproductive.&lt;/p&gt;
&lt;h3&gt;Do the Hard Work&lt;/h3&gt;
&lt;p&gt;Avoid doing only the easy tasks that yield minor results. Be prepared to refactor application source code and modify core architectures for meaningful impact.&lt;/p&gt;
&lt;h3&gt;Optimize Upstream (The Frontend/Edge First)&lt;/h3&gt;
&lt;p&gt;If you optimize the upstream components and successfully reduce the number of requests sent downstream, you achieve massive leverage. This often eliminates the need to optimize downstream components altogether. Fix things closest to the user first.&lt;/p&gt;
&lt;h3&gt;Optimize Downstream (The Core Dependencies Next)&lt;/h3&gt;
&lt;p&gt;Conversely, fixing a bottleneck at the deepest layer of your dependencies (e.g., optimizing a heavy database query in a shared platform API) will aggregate benefits across all consumer services at once.&lt;/p&gt;
&lt;h3&gt;Start with Low-Hanging Fruit for Momentum&lt;/h3&gt;
&lt;p&gt;Tackle easy, low-risk items first to build execution momentum. However, audit your backlog regularly to ensure you aren’t only doing the easy tasks.&lt;/p&gt;
&lt;h3&gt;Clean Up the Invisible Areas&lt;/h3&gt;
&lt;p&gt;When costs are visualized, people tend to only optimize what they can see on the graph. If you want to reduce Total Cost of Ownership (TCO), dig into the obscure, unallocated shared costs that are hard to attribute. Significant breakthroughs come from tackling these complex, ambiguous areas.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;8. Budget Management &amp;#x26; Continuous Forecasting&lt;/h2&gt;
&lt;h3&gt;Avoid Delayed Reporting&lt;/h3&gt;
&lt;p&gt;Traditional monthly tracking means looking at reports days or weeks after the billing cycle closes. While good for high-level trend analysis, it is too slow for incident response; by the time you notice a spike, the incident has already passed. Finding out after the fact is useless.&lt;/p&gt;
&lt;h3&gt;Real-Time Detection&lt;/h3&gt;
&lt;p&gt;Ensure you can detect anomalies the moment they occur using tools like AWS Cost Anomaly Detection.&lt;/p&gt;
&lt;h3&gt;Shorten the Review Cycle&lt;/h3&gt;
&lt;p&gt;During intensive cost-reduction phases, leverage AWS Cost Forecasting features. Review variances between your budgeted baseline and projected spend on a weekly or daily cycle rather than waiting for the month-end bill.&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>B2Bアプリでプロダクトマネージャーが漏らしがちな機能</title><link>https://veritycost.com/posts/b2b-app-features-pm-misses-11f78cd39306/</link><guid isPermaLink="true">https://veritycost.com/posts/b2b-app-features-pm-misses-11f78cd39306/</guid><description>B2BアプリケーションやSaaSには典型的なパターンがあり、あえて洞察しなくても実装が確定する機能は多くあります。</description><pubDate>Sun, 19 Jan 2025 06:33:02 GMT</pubDate><content:encoded>&lt;p&gt;B2BアプリケーションやSaaSには典型的なパターンがあり、あえて洞察しなくても実装が確定する機能は多くあります。&lt;/p&gt;
&lt;p&gt;＊優先度は洞察で決まります&lt;/p&gt;
&lt;p&gt;この記事では、いくつか見てきた中で、実装までなかなか至らずユーザーにフラストレーションが溜まりがちなものを主に述べてきます。&lt;/p&gt;
&lt;p&gt;なおエンジニア向けプロダクトは顧客層が特殊なため除きます。&lt;/p&gt;
&lt;h2&gt;業務に不可欠系&lt;/h2&gt;
&lt;p&gt;業務は反復が基本です。&lt;/p&gt;
&lt;p&gt;似たものを何度も入力をする手間を避けるには&lt;strong&gt;繰り返し入力機能&lt;/strong&gt;や&lt;strong&gt;テンプレート機能&lt;/strong&gt;、以前の作業をもとにした&lt;strong&gt;コピー機能&lt;/strong&gt;で効率化できます&lt;/p&gt;
&lt;p&gt;人は間違えるもの。なにかデータを追加する機能があれば、それに対する&lt;strong&gt;修正機能&lt;/strong&gt;と&lt;strong&gt;削除機能&lt;/strong&gt;は必ず発生します。CRUD(create, read, update, and delete)とはよく言ったものです。とにかく戻せるように(undo)。あえて実装しないなら問い合わせフローを考えておきましょう。&lt;/p&gt;
&lt;h3&gt;全文検索機能&lt;/h3&gt;
&lt;p&gt;業務は反復が基本です。過去の履歴をもとに、真似して仕事をしたいもの。&lt;/p&gt;
&lt;p&gt;そのためには検索が必要です。項目ごとの検索も便利ですが、全項目を一括して検索できる全文検索が一つのゴールです。&lt;/p&gt;
&lt;p&gt;考慮点として全文検索は運用コストとエンジニア工数がかかることが多いことが挙げられます。
より柔軟な検索ほどコストと工数が上がる傾向にあります。&lt;/p&gt;
&lt;h3&gt;漏れがちな背景&lt;/h3&gt;
&lt;p&gt;toBアプリケーションはオペレーションに密接するためオペレーション業務経験がないと難しいのかもしれません。&lt;/p&gt;
&lt;h2&gt;管理系機能&lt;/h2&gt;
&lt;p&gt;enterprise readyという意味でも大事です&lt;/p&gt;
&lt;h3&gt;アカウント関係&lt;/h3&gt;
&lt;p&gt;例えばプロダクトの新規導入時や異動の季節には少なくとも数十人単位で登録が必要になり、&lt;strong&gt;一括登録機能&lt;/strong&gt;がないと手が回りません。修正/削除も同様です。修正のためには一括出力機能も必要です。&lt;/p&gt;
&lt;p&gt;ビジネスでは複数の部署やチームに所属することがあります。しかし素朴な実装だと一人は一つの場所にしか所属しない制約となっています。
ここで問題なのが、この制約を外すにはアプリケーションおよびデータベースに大幅な改修が発生することです。場合によってはサービスのメンテナンス時間が必要になります。一番最初から対応していれば…となりますが経験がないとなかなか難しい。&lt;/p&gt;
&lt;h3&gt;監査ログ&lt;/h3&gt;
&lt;p&gt;ここに大体書いてありますが補足すると&lt;/p&gt;
&lt;p&gt;なんらかの不正やセキュリティ侵害を調査する際に行動ログは必須です。いつどこで誰がなにをしたのかトレースできる必要があります。&lt;/p&gt;
&lt;p&gt;速やかに特定できるよう、このログだけで完結できるほど特定しやすいことやフィルターできるとよいですね。&lt;/p&gt;
&lt;p&gt;人が思うより(小さい)不正は一般的です。わかりやすいのは営業機密の窃盗でしょうか。&lt;/p&gt;
&lt;p&gt;仮に機能が存在しないと、膨大なエンジニア工数を使って精度が低いログを毎回急ぎの差し込みで提供する羽目になります。提供方法もファイルなら経路を考える必要があります。&lt;/p&gt;
&lt;p&gt;監査ログ機能は運用コストとエンジニア工数が大きくかかります。また、複数プロダクトではインターフェイスを揃える部分が難関です。上記の通り急ぎの需要が発生することがあり、ユーザーから見てプロダクトごとにバラバラでは不都合でしょう。&lt;/p&gt;
&lt;h3&gt;漏れがちな背景&lt;/h3&gt;
&lt;p&gt;PdMは日常的に他社サービスを探求していると思いますが、どうしてもtoCが多くなります。個人ではエンタープライズアプリケーションを触る機会はまずないでしょう。仮に経験があったとしても管理機能の操作権限がなくその部分は触ったことがないのではないでしょうか。&lt;/p&gt;
&lt;p&gt;社内で利用しているサービスの権限を一時的にもらって操作してみるのが一番ですね&lt;/p&gt;
&lt;h2&gt;本番でのテスト系機能&lt;/h2&gt;
&lt;p&gt;ユーザーには直接影響ありませんが…&lt;/p&gt;
&lt;p&gt;リリース後や不具合発生時に、本番で検証できるアカウントがあると便利です。&lt;/p&gt;
&lt;p&gt;問題は、この本番でのテストユーザーがトラッキングやKPIといった部分に影響してしまうことです。各所共通のテストアカウントフラグと除外設定を準備したいところ。&lt;/p&gt;
&lt;p&gt;KPIが歪むほか、テストユーザーの動作によるトラブルが起き得ます。&lt;/p&gt;
&lt;p&gt;この件に関してはエンジニアからも提案すべきですがなかなか手が回らずボールが落ちがちです。&lt;/p&gt;
&lt;h2&gt;デモ機能&lt;/h2&gt;
&lt;p&gt;ユーザーには直接影響ありませんが…&lt;/p&gt;
&lt;p&gt;B2Bだと見込み顧客にセールスまたはマーケティングによるデモを挟むことが多いでしょう。このデモをどのアカウントで行うのでしょうか。本番環境でしょうか？&lt;/p&gt;
&lt;p&gt;アカウント準備、シナリオとそのデータ準備、保守が必要です。デモ環境に対するリリースはどのようにすべきでしょうか。開発中の機能をチラ見せしたいこともあれば、デモで使う環境はデモ中は安定していてほしいという。&lt;/p&gt;
&lt;p&gt;セールスエンジニアを採用していればある程度設定を任せられますが、存在しないならセールス等が直接スムーズに設定できるようにする必要があります。&lt;/p&gt;
&lt;h2&gt;その他&lt;/h2&gt;
&lt;p&gt;アプリケーションのパフォーマンス(速度)改善やサイト解析ツール系の導入、サポート業務に資するトラブルシューティング系機能も現実的には漏れがちです&lt;/p&gt;
&lt;h2&gt;最後に&lt;/h2&gt;
&lt;p&gt;海外のSaaSアプリケーションをいろいろ触ってみるのがおすすめです。オンボーディングから差が出ています。
管理系機能実装をまるっとお任せできるようなSaaSもあるので触ってみたり、機能一覧だけでも眺めてみたり、導入したりするのも一案です。&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>外資系企業なら会社のお金で寄付を増やせるかもしれません。Benevityを確認しよう。</title><link>https://veritycost.com/posts/benevity-7ea4541c/</link><guid isPermaLink="true">https://veritycost.com/posts/benevity-7ea4541c/</guid><description>Benevityとは何か、寄付プラットフォームとして利用するうえでの概要と特徴について</description><pubDate>Fri, 31 Jul 2026 03:00:00 GMT</pubDate><content:encoded>&lt;p&gt;外資系企業では、会社が寄付を上乗せしてくれる福利厚生として Benevity を導入していることがあります。利用すると、自分が1万円寄付した場合に会社がさらに1万円寄付する（マッチング寄付）など、自己負担を増やさず寄付額を大きくできます。&lt;/p&gt;
&lt;p&gt;できること&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;企業負担の寄付（Giving Credits）&lt;/li&gt;
&lt;li&gt;マッチング寄付（社員の寄付に会社が上乗せ）&lt;/li&gt;
&lt;li&gt;ボランティア時間の記録・マッチング&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;寄付できる対象&lt;/h2&gt;
&lt;p&gt;多数の国内の団体に対して寄付が可能です。もちろん国外も含まれます。&lt;/p&gt;
&lt;p&gt;日本赤十字や国境なき医師団、ユニセフといった著名どころはもちろん、
災害支援として、日本および各国の緊急募金にも寄付可能です。&lt;/p&gt;
&lt;p&gt;自然/科学系ならWWF Japanや京都大学iPS細胞研究財団など。&lt;/p&gt;
&lt;p&gt;テック系も揃っています。たとえば、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Apache Software Foundation&lt;/li&gt;
&lt;li&gt;Python Software Foundation&lt;/li&gt;
&lt;li&gt;Unicode Consortium&lt;/li&gt;
&lt;li&gt;日本Rubyの会&lt;/li&gt;
&lt;li&gt;NPO法人Waffle&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://www.apache.org/&quot;&gt;Apache Software Foundation&lt;/a&gt; (apache.org)&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.unicode.org/consortium/consort.html&quot;&gt;Unicode Consortium&lt;/a&gt; (unicode.org)&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://ruby-no-kai.org/&quot;&gt;日本Rubyの会&lt;/a&gt; (ruby-no-kai.org)&lt;/p&gt;
&lt;p&gt;その他地元の団体なども多くあります。&lt;/p&gt;
&lt;h3&gt;寄付可能団体リスト&lt;/h3&gt;
&lt;p&gt;おおよそ日本では認定NPOや公益財団法人などが多い印象です。&lt;/p&gt;
&lt;p&gt;Benevityは&lt;a href=&quot;https://www.techsoupjapan.org&quot;&gt;techsoup&lt;/a&gt;などの非営利団体データベースを利用して寄付先を管理しており、実際に表示される団体は各企業のポリシーによって異なります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Benevityを経由して寄付したい組織
   │
Benevity
   │
TechSoup
   │
各国の非営利団体データ
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;団体名の検索方法&lt;/h2&gt;
&lt;p&gt;データベースが正規化されていないため検索のやり方によって精度が異なります。英語または日本語で検索してみましょう。英名がない場合はローマ字のこともあります。&lt;/p&gt;
&lt;h2&gt;マッチング寄付を諦める前に&lt;/h2&gt;
&lt;p&gt;すでに継続的に寄附している団体がある場合、切り替えるのは若干手間がかかります。
また、benevityは日本の寄付金控除が利用できないこともあります。&lt;/p&gt;
&lt;p&gt;その場合、領収書をアップロードすることでマッチングさせることができます。
request a matching画面からアップロードすると、OCRにより団体名金額日付などを入力されます。
ただ、日本語の領収書がほとんどでしょうから、自分の名前が日英表記として差異が出てしまいます。
そこはコメント欄などで補足しましょう。&lt;/p&gt;
&lt;p&gt;どれくらい遡って領収書が提示できるかは組織の設定次第です。管理者に確認ください&lt;/p&gt;
&lt;h2&gt;さいごに&lt;/h2&gt;
&lt;p&gt;benevityを使い寄付を無理なく続けましょう&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>イギリスの街で見かけたイギリス英語</title><link>https://veritycost.com/posts/british-english-b17e1a2c/</link><guid isPermaLink="true">https://veritycost.com/posts/british-english-b17e1a2c/</guid><description>綴り差(centreなど)や単語差は有名なのでそれ以外で。地域差はあると思いますが大きめの街の傾向ということで。イギリス英語だけではなくイギリスの英語も含む</description><pubDate>Thu, 12 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;綴り差(centreなど)や単語差は有名なのでそれ以外で。地域差はあると思いますが大きめの街の傾向ということで。イギリス英語だけではなくイギリスの英語も含む。&lt;/p&gt;
&lt;h2&gt;店でのHow are you -&gt; Thank you&lt;/h2&gt;
&lt;p&gt;チェーンなどで店員がHow are you?と聞いているように見えます&lt;/p&gt;
&lt;p&gt;といっても特に意味はなく、(Good.というかgというかgすら消えて) Thank youと返すことが多いです&lt;/p&gt;
&lt;p&gt;せいぜいこんにちは、どうもぐらいの用途です。
やはり会話はシチュエーションがあってそこでの定型応答が大事ですね&lt;/p&gt;
&lt;h2&gt;Please&lt;/h2&gt;
&lt;p&gt;アメリカではあまりpleaseが出てくる印象はなく、特定のフレーズとセットで使うことが多い認識です(コロケーション)&lt;/p&gt;
&lt;p&gt;イギリスでは指示の文章に非常によく出てきます。pleaseだらけです。&lt;/p&gt;
&lt;h2&gt;Point&lt;/h2&gt;
&lt;figure class=&quot;media-figure&quot; style=&quot;max-width: 360px; margin: 1rem auto 1.25rem;&quot;&gt;
  ![help point](../../assets/british-english.jpg)
  &lt;figcaption&gt;pointに集約&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;設備の場所をpointと呼ぶことが多いです&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;電話する場所: call point
消火器などの場所: fire point
待ち合わせ場所: meeting point
支払場所: payment point
非常用インターフォン: help point
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;まだまだ無限に出てきます&lt;/p&gt;
&lt;p&gt;アメリカだと設備名を書くことが多い印象です。それこそ「消火器」など。場所をポイントと呼ぶのはなにか重要なイベントがあるような場所ぐらいかなと思います&lt;/p&gt;
&lt;h2&gt;bun&lt;/h2&gt;
&lt;p&gt;アメリカの店で見かけるbunといえば、小さくて丸いパン状の生地のもの。◯◯ bunとすることで可能性が広がります。具材、用途、属性…。結果として具が入ってたり載せたりすることも多くなります。&lt;/p&gt;
&lt;figure class=&quot;media-figure&quot; style=&quot;max-width: 100px; margin: 1rem auto 1.25rem;&quot;&gt;
  ![british bun](../../assets/british-english-bun.svg)
  &lt;figcaption&gt;これはbun.sh&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;イギリスでは焼いた甘い小型の菓子パン／ティーパンとして定着しています。小型なのは同じですがおやつ前提の印象です。そのため、英国話者にとってbun.shのマスコットに違和感を覚える人が少なくなさそうです。&lt;/p&gt;
&lt;h2&gt;collection / collect&lt;/h2&gt;
&lt;p&gt;紙チケットを受け取るならcollect、駅でもチケットはcollectとcollectが大量に出てきます。受け取る、発券する、引き換えるならcollect。アメリカならpickupやprintでしょう。&lt;/p&gt;
&lt;h2&gt;call at&lt;/h2&gt;
&lt;p&gt;電車などで駅に停車するならcall at。アメリカならstop atあたりが多いかと&lt;/p&gt;
&lt;h2&gt;reduce&lt;/h2&gt;
&lt;p&gt;saleでもdiscountでもoffでもなくreduced。とにかくreduce&lt;/p&gt;
&lt;h2&gt;bylaws&lt;/h2&gt;
&lt;p&gt;イギリスではbylaws表記がちょくちょく出てきます。条例含め、地方自治体・公共機関が定めるローカル規則ですね。一方アメリカでは bylaws は主に会社や団体の内部規則を意味するため、街中で掲示されていると違和感を覚えやすい語です。&lt;/p&gt;
&lt;h2&gt;service&lt;/h2&gt;
&lt;p&gt;特に公共インフラでなんでもservice。行為全体を指す&lt;/p&gt;
&lt;h2&gt;obstruct / obstruction&lt;/h2&gt;
&lt;p&gt;特に公共インフラで、具体的より抽象的に伝える&lt;/p&gt;
&lt;h2&gt;店名につくfone&lt;/h2&gt;
&lt;p&gt;phoneのこと。つまり携帯電話関係の店。発音と綴りの一致化で親しみやすくした？&lt;/p&gt;
&lt;h2&gt;two courseなど&lt;/h2&gt;
&lt;p&gt;2品構成のコースであり、前菜かデザート + メイン。コースが2個ではない。ドリンクが含まれることもある&lt;/p&gt;
&lt;h2&gt;bill / check&lt;/h2&gt;
&lt;p&gt;店ならイギリスはbill。アメリカはcheck&lt;/p&gt;
&lt;h2&gt;-bury / -bery / berry / burry / -borough / -brough / -burgh など&lt;/h2&gt;
&lt;p&gt;地名にやたら出てきます。防御された場所・城塞・囲われた集落を指し、ドイツ語の -burg（ブルク）と同じ。-tonもそうですが同じようなパターンの地名が大量にあります。(聖人名/人名/都市名)+地形語&lt;/p&gt;
&lt;h2&gt;地名のRussell&lt;/h2&gt;
&lt;p&gt;ありふれた苗字でもあり、有力貴族の苗字でもあり、著名人も多く排出している。
なのでRussellがついた地名が増殖する&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>Julian map と packed table による高速な civil_from_days</title><link>https://veritycost.com/posts/civil-from-days-julian-map-packed-table-c0ecbb5f/</link><guid isPermaLink="true">https://veritycost.com/posts/civil-from-days-julian-map-packed-table-c0ecbb5f/</guid><description>days_from_civil の逆変換 civil_from_days を、Julian map と packed table を用いて 32-bit 整数演算のみで高速化する</description><pubDate>Fri, 28 Aug 2026 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Unix epochからの通算日を年月日へ戻す処理は、一般に &lt;code&gt;civil_from_days&lt;/code&gt; と呼ばれます。前の記事で扱った &lt;code&gt;days_from_civil&lt;/code&gt; の逆変換です。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;civil date -- days_from_civil --&gt; epoch day
civil date &amp;#x3C;-- civil_from_days --- epoch day
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;この記事では出力年を &lt;code&gt;1..9999&lt;/code&gt; に限定し、Ben JoffeのJulian mapによる年復元と、366要素のpacked month/day tableを組み合わせます。Bunではさらに、二つの定数除算を上方丸めしたbinary64逆数との乗算へ置き換えます。&lt;/p&gt;
&lt;p&gt;Apple Silicon上の今回の測定では、実行時間が通常のHoward Hinnant実装の約13%（Bun）、26%（Rust）、30%（C）になりました。削減率ではそれぞれ約87%、74%、70%です。&lt;/p&gt;
&lt;h2&gt;前提&lt;/h2&gt;
&lt;p&gt;入力範囲は、暦年 &lt;code&gt;1..9999&lt;/code&gt; に対応する次のUnix epoch dayです。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;-719162 &amp;#x3C;= epochDay &amp;#x3C;= 2932896
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;両端はそれぞれ &lt;code&gt;0001-01-01&lt;/code&gt; と &lt;code&gt;9999-12-31&lt;/code&gt; です。&lt;/p&gt;
&lt;h2&gt;TypeScript実装&lt;/h2&gt;
&lt;p&gt;Bun向けのホットパスは、年月日を一つの整数へpackして返します。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;packed = year * 512 + month * 32 + day
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;月は &lt;code&gt;1..12&lt;/code&gt;、日は &lt;code&gt;1..31&lt;/code&gt; なので、下位9 bitへ月日を格納できます。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const INV_146097 = (1 / 146_097) * (1 + Number.EPSILON);
const INV_1461 = (1 / 1_461) * (1 + Number.EPSILON);

const MONTH_DAY = new Uint16Array(366);

for (let dayOfMarchYear = 0; dayOfMarchYear &amp;#x3C;= 365; dayOfMarchYear++) {
  const n = 2_141 * dayOfMarchYear + 197_913;
  const marchMonth = n &gt;&gt;&gt; 16;
  const day = Math.floor((n &amp;#x26; 65_535) / 2_141) + 1;
  const janOrFeb = dayOfMarchYear &gt;= 306;
  const month = janOrFeb ? marchMonth - 12 : marchMonth;

  MONTH_DAY[dayOfMarchYear] =
    (Number(janOrFeb) &amp;#x3C;&amp;#x3C; 9) | (month &amp;#x3C;&amp;#x3C; 5) | day;
}

/**
 * Returns year * 512 + month * 32 + day.
 * Preconditions: -719162 &amp;#x3C;= epochDay &amp;#x3C;= 2932896.
 */
export function civilFromDaysPacked(epochDay: number): number {
  const q = ((epochDay &amp;#x3C;&amp;#x3C; 2) + 2_877_875) | 0;
  const century = (q * INV_146097) | 0;
  const julian =
    (q + ((century - (century &gt;&gt; 2)) &amp;#x3C;&amp;#x3C; 2)) | 0;
  const year = (julian * INV_1461) | 0;
  const remainder =
    (julian - Math.imul(year, 1_461)) | 0;

  return (
    (year &amp;#x3C;&amp;#x3C; 9) + MONTH_DAY[remainder &gt;&gt;&gt; 2]
  ) | 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通常のオブジェクトが必要なら、ホットループの外で展開できます。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;export function unpackCivilDate(packed: number) {
  return {
    year: Math.floor(packed / 512),
    month: (packed &gt;&gt;&gt; 5) &amp;#x26; 15,
    day: packed &amp;#x26; 31,
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;civilFromDaysPacked&lt;/code&gt; 自体にオブジェクト生成を含めると、カレンダー計算より割り当てコストを測る比率が大きくなるため、ベンチマークではpackした整数を返しています。&lt;/p&gt;
&lt;h2&gt;Rust実装&lt;/h2&gt;
&lt;p&gt;Rustではtableをコンパイル時に生成できます。定数除算はそのまま記述し、最適化をコンパイラに任せます。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct CivilDate {
    pub year: i32,
    pub month: u32,
    pub day: u32,
}

const fn build_month_day_table() -&gt; [u16; 366] {
    let mut table = [0u16; 366];
    let mut r = 0usize;

    while r &amp;#x3C;= 365 {
        let n = 2_141 * r as u32 + 197_913;
        let march_month = n &gt;&gt; 16;
        let day = (n &amp;#x26; 65_535) / 2_141 + 1;
        let month = if r &gt;= 306 {
            march_month - 12
        } else {
            march_month
        };

        let year_bump = u16::from(r &gt;= 306);
        table[r] = (year_bump &amp;#x3C;&amp;#x3C; 9) | ((month &amp;#x3C;&amp;#x3C; 5) | day) as u16;
        r += 1;
    }

    table
}

const MONTH_DAY: [u16; 366] = build_month_day_table();

#[inline]
pub fn civil_from_days(epoch_day: i32) -&gt; CivilDate {
    let q = 4 * (epoch_day + 719_468) as u32 + 3;
    let century = q / 146_097;
    let julian = q + century * 3 + (century &amp;#x26; 3);
    let year = julian / 1_461;
    let day_of_march_year = julian % 1_461 / 4;
    let packed = MONTH_DAY[day_of_march_year as usize] as u32;

    CivilDate {
        year: (year + (packed &gt;&gt; 9)) as i32,
        month: (packed &gt;&gt; 5) &amp;#x26; 15,
        day: packed &amp;#x26; 31,
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;C実装&lt;/h2&gt;
&lt;p&gt;Cでは次の初期化をプログラム開始時に一度実行します。本番用途では、生成結果を &lt;code&gt;static const uint16_t[366]&lt;/code&gt; として埋め込めば初期化も不要です。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;#include &amp;#x3C;stdint.h&gt;

typedef struct {
    int32_t year;
    uint32_t month;
    uint32_t day;
} civil_date_t;

static uint16_t month_day[366];

static void init_month_day_table(void) {
    for (uint32_t r = 0; r &amp;#x3C;= 365; ++r) {
        const uint32_t n = 2141u * r + 197913u;
        const uint32_t march_month = n &gt;&gt; 16;
        const uint32_t day = (n &amp;#x26; 65535u) / 2141u + 1u;
        const uint32_t month = r &gt;= 306u
            ? march_month - 12u
            : march_month;

        month_day[r] = (uint16_t)(
            ((r &gt;= 306u) &amp;#x3C;&amp;#x3C; 9) | (month &amp;#x3C;&amp;#x3C; 5) | day
        );
    }
}

static inline civil_date_t civil_from_days(int32_t epoch_day) {
    const uint32_t q = 4u * (uint32_t)(epoch_day + 719468) + 3u;
    const uint32_t century = q / 146097u;
    const uint32_t julian = q + century * 3u + (century &amp;#x26; 3u);
    const uint32_t year = julian / 1461u;
    const uint32_t day_of_march_year = julian % 1461u / 4u;
    const uint32_t packed = month_day[day_of_march_year];

    return (civil_date_t) {
        (int32_t)(year + (packed &gt;&gt; 9)),
        (packed &gt;&gt; 5) &amp;#x26; 15u,
        packed &amp;#x26; 31u
    };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;通常の &lt;code&gt;civil_from_days&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Howard Hinnantの基準実装をTypeScriptで書くと次のようになります。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function civilFromDaysHinnant(epochDay: number) {
  const z = epochDay + 719_468;
  const era = Math.floor(z / 146_097);
  const dayOfEra = z - era * 146_097;
  const yearOfEra = Math.floor(
    (dayOfEra -
      Math.floor(dayOfEra / 1_460) +
      Math.floor(dayOfEra / 36_524) -
      Math.floor(dayOfEra / 146_096)) /
      365,
  );
  const year = yearOfEra + era * 400;
  const dayOfYear =
    dayOfEra -
    (365 * yearOfEra +
      Math.floor(yearOfEra / 4) -
      Math.floor(yearOfEra / 100));
  const marchMonth = Math.floor((5 * dayOfYear + 2) / 153);
  const day =
    dayOfYear - Math.floor((153 * marchMonth + 2) / 5) + 1;
  const month = marchMonth + (marchMonth &amp;#x3C; 10 ? 3 : -9);

  return {
    year: year + (month &amp;#x3C;= 2 ? 1 : 0),
    month,
    day,
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これは広い年範囲を扱える、明快で移植性の高い実装です。今回の限定版は、この汎用性を固定範囲での速度と交換します。&lt;/p&gt;
&lt;h2&gt;Julian mapで年を復元する&lt;/h2&gt;
&lt;p&gt;今回の年復元はBen JoffeのJulian mapを利用します。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;q       = 4 * (epochDay + 719468) + 3
century = floor(q / 146097)
julian  = q + 3 * century + (century &amp;#x26; 3)
year    = floor(julian / 1461)
rem     = floor((julian - 1461 * year) / 4)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Gregorian calendarの400年周期を補正した後、一時的にJulian calendarの4年周期へ写すことで、年復元の依存鎖を短くします。&lt;/p&gt;
&lt;p&gt;元のJoffe式 &lt;code&gt;q - (century &amp;#x26; ~3) + 4 * century&lt;/code&gt; は、&lt;code&gt;century = (century &amp;#x26; ~3) + (century &amp;#x26; 3)&lt;/code&gt; を使うと、上の短い式へ整理できます。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;1461 = 4 * 365 + 1&lt;/code&gt; なので、&lt;code&gt;julian / 1461&lt;/code&gt; がMarch-basedの年を返し、余りを4で割った値がMarch 1から数えた年内日になります。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 &amp;#x3C;= rem &amp;#x3C;= 365
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;1月と2月はMarch-based yearの末尾にあります。&lt;code&gt;rem &gt;= 306&lt;/code&gt; の年補正をtableのbit 9へ格納することで、実行時の比較と加算も除去します。&lt;/p&gt;
&lt;h2&gt;月日を366要素のtableから取得する&lt;/h2&gt;
&lt;p&gt;通常は年内日から月日を復元するために、乗算、シフト、除算、1月・2月の補正を行います。しかし &lt;code&gt;rem&lt;/code&gt; は &lt;code&gt;0..365&lt;/code&gt; の366通りしかありません。&lt;/p&gt;
&lt;p&gt;各値に対応する年補正、月、日を次の10-bit整数へ格納します。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(yearBump &amp;#x3C;&amp;#x3C; 9) | (month &amp;#x3C;&amp;#x3C; 5) | day
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;取得は次だけです。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const packed = MONTH_DAY[dayOfMarchYear];
const year = marchYear + (packed &gt;&gt;&gt; 9);
const month = (packed &gt;&gt;&gt; 5) &amp;#x26; 15;
const day = packed &amp;#x26; 31;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;tableの大きさは、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;366 * 2 = 732 bytes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;です。一般的なPCのL1 cacheには十分小さい一方、cold cache、組み込み環境、コードサイズを重視する用途では算術版が有利な可能性があります。&lt;/p&gt;
&lt;h2&gt;Bunで除算を上方丸め逆数へ置き換える&lt;/h2&gt;
&lt;p&gt;Bunでは &lt;code&gt;/146097&lt;/code&gt; と &lt;code&gt;/1461&lt;/code&gt; より、あらかじめ求めた逆数との乗算が高速でした。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const INV_146097 = (1 / 146_097) * (1 + Number.EPSILON);
const INV_1461 = (1 / 1_461) * (1 + Number.EPSILON);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;実際のbinary64値は次のとおりです。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;INV_146097 = 0.0000068447675174712704
bits        = 0x3edcb5835e647c33

INV_1461   = 0.00068446269678302542
bits        = 0x3f466db072f2284e
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通常の最も近いbinary64逆数では、割り切れる入力の積が整数よりわずかに小さくなり、&lt;code&gt;Math.floor&lt;/code&gt; が1小さい値を返す場合があります。&lt;code&gt;1 + Number.EPSILON&lt;/code&gt; を掛け、真の逆数よりわずかに大きい隣接値を使うことでこれを避けます。&lt;/p&gt;
&lt;p&gt;どちらも、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;INV_d * d - 1 = 2^-52
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;です。対象範囲の最大商は約100と10000なので、上方誤差はそれぞれ約 &lt;code&gt;2.3e-14&lt;/code&gt; と &lt;code&gt;2.3e-12&lt;/code&gt; 以下です。一方、割り切れない整数入力が次の商境界まで持つ距離は少なくとも &lt;code&gt;1/146097&lt;/code&gt; と &lt;code&gt;1/1461&lt;/code&gt; です。したがって、上方誤差によって次の整数へ到達することはありません。&lt;/p&gt;
&lt;p&gt;この議論は指定範囲とIEEE-754 binary64に依存します。範囲を広げる場合は再証明と再検証が必要です。&lt;/p&gt;
&lt;p&gt;また、二つの積は非負で2³¹未満の整数へ切り捨てられるため、Bun版では &lt;code&gt;Math.floor&lt;/code&gt; を32-bit整数化の &lt;code&gt;| 0&lt;/code&gt; に置き換えられます。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const century = (q * INV_146097) | 0;
const year = (julian * INV_1461) | 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最終版では、&lt;code&gt;q&lt;/code&gt; の定数部分も畳み込み、シフト、積、戻り値をint32ドメインへ閉じています。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const q = ((epochDay &amp;#x3C;&amp;#x3C; 2) + 2_877_875) | 0;
const remainder =
  (julian - Math.imul(year, 1_461)) | 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;また、非負の &lt;code&gt;century&lt;/code&gt; について、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;3c + (c &amp;#x26; 3) = 4 * (c - floor(c / 4))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;なので、Bun版では次の別形を使います。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const julian =
  (q + ((century - (century &gt;&gt; 2)) &amp;#x3C;&amp;#x3C; 2)) | 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これは今回のBunでは有効でしたが、最適化されたC/Rustではコンパイラが元の式をすでに同等の命令列へ変換できます。一般にこちらが速いと主張するものではありません。&lt;/p&gt;
&lt;h2&gt;追加tableなしの4-wide SIMD版&lt;/h2&gt;
&lt;p&gt;複数の日付をまとめて変換できる場合、732-byte tableをさらに大きくする代わりに、月日復元を算術式へ戻して4入力をNEONで並列処理できます。&lt;/p&gt;
&lt;p&gt;January/February補正は、packed表現では次の一回の加算になります。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;year += 1, month -= 12

packed += 512 - 12 * 32
        = 128
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Clangの4要素vector型で中心部分を書くと次の形です。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;typedef uint32_t u32x4 __attribute__((ext_vector_type(4)));

static inline u32x4 civil_from_days_4(u32x4 epoch_day) {
    const u32x4 q = epoch_day * 4u + 2877875u;
    const u32x4 century = q / 146097u;
    const u32x4 julian = q + century * 3u + (century &amp;#x26; 3u);
    const u32x4 year = julian / 1461u;
    const u32x4 rem = (julian - year * 1461u) &gt;&gt; 2;
    const u32x4 n = rem * 2141u + 197913u;
    const u32x4 march_month = n &gt;&gt; 16;
    const u32x4 day = (n &amp;#x26; 65535u) / 2141u + 1u;
    const u32x4 jan_feb = (u32x4)(rem &gt;= 306u);

    return year * 512u + march_month * 32u + day
        + (jan_feb &amp;#x26; 128u);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apple Silicon／Clang &lt;code&gt;-O3 -march=native&lt;/code&gt; ではNEON命令へ展開されました。同じランダム入力配列を処理し、scalar側の自動vectorizeを無効にしたbatch比較は次のとおりです。&lt;/p&gt;




















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;算術版&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;ns/件&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;scalar比&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;scalar&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;2.694&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1.00倍&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4-wide SIMD&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;0.713&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;3.78倍&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;全3,652,059 epoch dayについてHinnant実装と一致しました。これはARM64向けbatch APIのスループットであり、1件を返すscalar APIやpacked table版のレイテンシと同一の比較ではありません。&lt;/p&gt;
&lt;h2&gt;32-bit整数の境界&lt;/h2&gt;
&lt;p&gt;入力範囲では、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;306 &amp;#x3C;= epochDay + 719468 &amp;#x3C;= 3652364
1227 &amp;#x3C;= q &amp;#x3C;= 14609459
0 &amp;#x3C;= century &amp;#x3C;= 99
0 &amp;#x3C;= julian &amp;#x3C; 14610000
0 &amp;#x3C;= year &amp;#x3C;= 9999
0 &amp;#x3C;= dayOfMarchYear &amp;#x3C;= 365
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;となり、整数部分は符号付き32-bitへ十分収まります。year bumpを含む &lt;code&gt;MONTH_DAY&lt;/code&gt; の各値は10 bit、table全体は732 bytesです。&lt;/p&gt;
&lt;h2&gt;全数検証&lt;/h2&gt;
&lt;p&gt;指定範囲のすべてのepoch dayについて、Hinnant実装と比較しました。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;2932896 - (-719162) + 1
= 3,652,059 inputs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;検証対象はHinnant、Neri–Schneider、Ben Joffe、Bunの上方逆数版、packed table版です。TypeScript、Rust、Cの最終実装は全件一致しました。&lt;/p&gt;
&lt;h2&gt;ベンチマーク&lt;/h2&gt;
&lt;p&gt;Apple Silicon ARM64上で、決定的な疑似乱数で生成した &lt;code&gt;2^20&lt;/code&gt; 件のepoch dayを24回処理しました。入力位置はラウンドごとに回転させています。5回測定した最小値です。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Bun 1.4.0
rustc 1.96.0 -C opt-level=3 -C target-cpu=native
Apple clang 21.0.0 -O3 -march=native
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;今回再測定した結果は次のとおりでした。&lt;/p&gt;

































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;環境&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;通常のHinnant&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;今回の限定版&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;実行時間削減率&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;スループット&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Bun&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;37.088 ns/件&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;4.830 ns/件&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;87.0%&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;7.68倍&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Rust&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;5.734 ns/件&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1.500 ns/件&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;73.8%&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;3.82倍&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;C&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;5.315 ns/件&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1.603 ns/件&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;69.8%&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;3.32倍&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Bunでは、year bumpをtableへ移しつつdouble演算を残した版が6.091 ns/件、int32ドメインへ閉じた最終版が4.830 ns/件でした。year bumpのtable化に加え、定数畳み込み、&lt;code&gt;Math.imul&lt;/code&gt;、シフトを使ってdoubleとint32の往復を減らした効果が出ています。&lt;/p&gt;
&lt;p&gt;RustとCでは、ソース上の定数除算をコンパイラが乗算とシフトへ変換できます。そのため、明示的な逆数より通常の整数除算表記が適切でした。packed tableによる月日復元の依存鎖削減が大きく効いています。&lt;/p&gt;
&lt;p&gt;これらは特定のCPU、コンパイラ、JIT、入力分布での結果です。別世代のApple Silicon、x86-64、Node.js/V8、ブラウザ、別バージョンのRustやClangでは必ず再測定してください。&lt;/p&gt;
&lt;h2&gt;先行研究と今回の位置づけ&lt;/h2&gt;
&lt;p&gt;この実装の年復元はBen JoffeのJulian mapを利用しています。Gregorian calendar変換を乗算とシフトへ変換する方法は、NeriとSchneiderがEuclidean affine functionとして体系化しています。Howard Hinnantの実装は基準となる汎用アルゴリズムです。&lt;/p&gt;
&lt;p&gt;したがって、Julian map、年復元、定数除算のstrength reduction、年内日tableのいずれも、単独の新規技法として主張するものではありません。&lt;/p&gt;
&lt;p&gt;今回の構成は次の限定特殊化です。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;出力年を &lt;code&gt;1..9999&lt;/code&gt; に限定する&lt;/li&gt;
&lt;li&gt;Ben JoffeのJulian mapで年を復元する&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0..365&lt;/code&gt; のMarch-based年内日を、year bumpも含む732-byteのpacked tableへ写す&lt;/li&gt;
&lt;li&gt;Bunでは範囲証明した上方丸めbinary64逆数を使う&lt;/li&gt;
&lt;li&gt;CとRustでは定数除算のstrength reductionをコンパイラに任せる&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;調査した公開実装の範囲では、この固定範囲、Bunの上方丸め逆数、Joffe式、packed month/day tableを組み合わせた同一構成は確認できませんでした。ただし、あらゆるCPU、言語、入力範囲で最速または最適であることを意味しません。&lt;/p&gt;
&lt;p&gt;正確な表現は次のとおりです。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Apple Silicon上の今回のBun、Rust、Cベンチマークにおいて、比較したHinnant、Neri–Schneider、Ben Joffe実装より高速だった。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;まとめ&lt;/h2&gt;
&lt;p&gt;年 &lt;code&gt;1..9999&lt;/code&gt; に対応するepoch dayへ範囲を限定すると、&lt;code&gt;civil_from_days&lt;/code&gt; は次の構成で高速化できました。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Julian mapでGregorianの400年補正を行う&lt;/li&gt;
&lt;li&gt;1461日のJulian 4年周期から年とMarch-based年内日を復元する&lt;/li&gt;
&lt;li&gt;366要素のpacked tableからyear bump、月、日を一度に取得する&lt;/li&gt;
&lt;li&gt;Bunでは二つの定数除算を上方丸め逆数との乗算へ置き換える&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;今回のApple Silicon測定では、実行時間が通常のHinnant実装の約13%（Bun）、26%（Rust）、30%（C）になりました。さらに、追加tableを使わないCの4-wide SIMD算術版は、scalar算術版の約3.78倍のbatch throughputでした。&lt;/p&gt;
&lt;p&gt;汎用コードではHinnant実装、tableを避けるならNeri–SchneiderまたはJoffeの算術版が適しています。入力範囲が保証され、732-byte tableが許容でき、変換がホットパスにある場合に今回の限定版が候補になります。&lt;/p&gt;
&lt;h2&gt;参考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Howard Hinnant, &lt;a href=&quot;https://howardhinnant.github.io/date_algorithms.html&quot;&gt;chrono-Compatible Low-Level Date Algorithms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Cassio Neri and Lorenz Schneider, &lt;a href=&quot;https://arxiv.org/abs/2102.06959&quot;&gt;Euclidean Affine Functions and Applications to Calendar Algorithms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Cassio Neri, &lt;a href=&quot;https://github.com/cassioneri/eaf&quot;&gt;EAF supplementary implementation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Ben Joffe, &lt;a href=&quot;https://www.benjoffe.com/fast-date&quot;&gt;The Julian Map&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Ben Joffe, &lt;a href=&quot;https://www.benjoffe.com/fast-date-64&quot;&gt;A Very Fast 64-Bit Date Algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Ben Joffe, &lt;a href=&quot;https://github.com/benjoffe/fast-date-benchmarks&quot;&gt;fast-date-benchmarks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://veritycost.com/en/posts/fast-days-from-civil-41f45490/&quot;&gt;マジックナンバー化による高速な &lt;code&gt;days_from_civil&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>Fast civil_from_days with Julian Map and Packed Table</title><link>https://veritycost.com/en/posts/civil-from-days-julian-map-packed-table-c18dd69a/</link><guid isPermaLink="true">https://veritycost.com/en/posts/civil-from-days-julian-map-packed-table-c18dd69a/</guid><description>Implementing civil_from_days using a Julian map and packed table for 32-bit integer-only computation with year range 1..9999</description><pubDate>Fri, 28 Aug 2026 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Converting a Unix epoch day back into a civil year, month, and day is commonly called &lt;code&gt;civil_from_days&lt;/code&gt;. It is the inverse of the &lt;code&gt;days_from_civil&lt;/code&gt; operation covered in the previous article.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;civil date -- days_from_civil --&gt; epoch day
civil date &amp;#x3C;-- civil_from_days --- epoch day
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This article restricts output years to &lt;code&gt;1..9999&lt;/code&gt; and combines Ben Joffe’s Julian-map year recovery with a 366-element packed month/day table. In Bun, two constant divisions are additionally replaced with multiplication by upward-rounded binary64 reciprocals.&lt;/p&gt;
&lt;p&gt;In the Apple Silicon benchmarks reported here, the bounded implementation reduced execution time by approximately 87% in Bun, 74% in Rust, and 70% in C compared with the usual Howard Hinnant implementation.&lt;/p&gt;
&lt;h2&gt;Preconditions&lt;/h2&gt;
&lt;p&gt;The input range corresponding to civil years &lt;code&gt;1..9999&lt;/code&gt; is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;-719162 &amp;#x3C;= epochDay &amp;#x3C;= 2932896
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The endpoints are &lt;code&gt;0001-01-01&lt;/code&gt; and &lt;code&gt;9999-12-31&lt;/code&gt;, respectively.&lt;/p&gt;
&lt;h2&gt;TypeScript Implementation&lt;/h2&gt;
&lt;p&gt;The Bun hot-path implementation returns the date packed into one integer:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;packed = year * 512 + month * 32 + day
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Months are in &lt;code&gt;1..12&lt;/code&gt; and days are in &lt;code&gt;1..31&lt;/code&gt;, so the month and day fit in the low nine bits.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const INV_146097 = (1 / 146_097) * (1 + Number.EPSILON);
const INV_1461 = (1 / 1_461) * (1 + Number.EPSILON);

const MONTH_DAY = new Uint16Array(366);

for (let dayOfMarchYear = 0; dayOfMarchYear &amp;#x3C;= 365; dayOfMarchYear++) {
  const n = 2_141 * dayOfMarchYear + 197_913;
  const marchMonth = n &gt;&gt;&gt; 16;
  const day = Math.floor((n &amp;#x26; 65_535) / 2_141) + 1;
  const janOrFeb = dayOfMarchYear &gt;= 306;
  const month = janOrFeb ? marchMonth - 12 : marchMonth;

  MONTH_DAY[dayOfMarchYear] =
    (Number(janOrFeb) &amp;#x3C;&amp;#x3C; 9) | (month &amp;#x3C;&amp;#x3C; 5) | day;
}

/**
 * Returns year * 512 + month * 32 + day.
 * Preconditions: -719162 &amp;#x3C;= epochDay &amp;#x3C;= 2932896.
 */
export function civilFromDaysPacked(epochDay: number): number {
  const q = ((epochDay &amp;#x3C;&amp;#x3C; 2) + 2_877_875) | 0;
  const century = (q * INV_146097) | 0;
  const julian =
    (q + ((century - (century &gt;&gt; 2)) &amp;#x3C;&amp;#x3C; 2)) | 0;
  const year = (julian * INV_1461) | 0;
  const remainder =
    (julian - Math.imul(year, 1_461)) | 0;

  return (
    (year &amp;#x3C;&amp;#x3C; 9) + MONTH_DAY[remainder &gt;&gt;&gt; 2]
  ) | 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If an ordinary object is needed, unpack it outside the hot loop:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;export function unpackCivilDate(packed: number) {
  return {
    year: Math.floor(packed / 512),
    month: (packed &gt;&gt;&gt; 5) &amp;#x26; 15,
    day: packed &amp;#x26; 31,
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Allocating an object inside &lt;code&gt;civilFromDaysPacked&lt;/code&gt; would make object allocation a significant part of the benchmark. Packing the result keeps the measurement focused on the calendar conversion itself.&lt;/p&gt;
&lt;h2&gt;Rust Implementation&lt;/h2&gt;
&lt;p&gt;Rust can generate the table at compile time. The constant divisions are left as &lt;code&gt;/&lt;/code&gt;, allowing the compiler to perform its usual strength reduction.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct CivilDate {
    pub year: i32,
    pub month: u32,
    pub day: u32,
}

const fn build_month_day_table() -&gt; [u16; 366] {
    let mut table = [0u16; 366];
    let mut r = 0usize;

    while r &amp;#x3C;= 365 {
        let n = 2_141 * r as u32 + 197_913;
        let march_month = n &gt;&gt; 16;
        let day = (n &amp;#x26; 65_535) / 2_141 + 1;
        let month = if r &gt;= 306 {
            march_month - 12
        } else {
            march_month
        };

        let year_bump = u16::from(r &gt;= 306);
        table[r] = (year_bump &amp;#x3C;&amp;#x3C; 9) | ((month &amp;#x3C;&amp;#x3C; 5) | day) as u16;
        r += 1;
    }

    table
}

const MONTH_DAY: [u16; 366] = build_month_day_table();

#[inline]
pub fn civil_from_days(epoch_day: i32) -&gt; CivilDate {
    let q = 4 * (epoch_day + 719_468) as u32 + 3;
    let century = q / 146_097;
    let julian = q + century * 3 + (century &amp;#x26; 3);
    let year = julian / 1_461;
    let day_of_march_year = julian % 1_461 / 4;
    let packed = MONTH_DAY[day_of_march_year as usize] as u32;

    CivilDate {
        year: (year + (packed &gt;&gt; 9)) as i32,
        month: (packed &gt;&gt; 5) &amp;#x26; 15,
        day: packed &amp;#x26; 31,
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;C Implementation&lt;/h2&gt;
&lt;p&gt;In this C version, the initialization routine must run once at program startup. Production code can instead embed the generated values in a &lt;code&gt;static const uint16_t[366]&lt;/code&gt;, eliminating runtime initialization.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;#include &amp;#x3C;stdint.h&gt;

typedef struct {
    int32_t year;
    uint32_t month;
    uint32_t day;
} civil_date_t;

static uint16_t month_day[366];

static void init_month_day_table(void) {
    for (uint32_t r = 0; r &amp;#x3C;= 365; ++r) {
        const uint32_t n = 2141u * r + 197913u;
        const uint32_t march_month = n &gt;&gt; 16;
        const uint32_t day = (n &amp;#x26; 65535u) / 2141u + 1u;
        const uint32_t month = r &gt;= 306u
            ? march_month - 12u
            : march_month;

        month_day[r] = (uint16_t)(
            ((r &gt;= 306u) &amp;#x3C;&amp;#x3C; 9) | (month &amp;#x3C;&amp;#x3C; 5) | day
        );
    }
}

static inline civil_date_t civil_from_days(int32_t epoch_day) {
    const uint32_t q = 4u * (uint32_t)(epoch_day + 719468) + 3u;
    const uint32_t century = q / 146097u;
    const uint32_t julian = q + century * 3u + (century &amp;#x26; 3u);
    const uint32_t year = julian / 1461u;
    const uint32_t day_of_march_year = julian % 1461u / 4u;
    const uint32_t packed = month_day[day_of_march_year];

    return (civil_date_t) {
        (int32_t)(year + (packed &gt;&gt; 9)),
        (packed &gt;&gt; 5) &amp;#x26; 15u,
        packed &amp;#x26; 31u
    };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The Usual &lt;code&gt;civil_from_days&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Howard Hinnant’s reference implementation can be written in TypeScript as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function civilFromDaysHinnant(epochDay: number) {
  const z = epochDay + 719_468;
  const era = Math.floor(z / 146_097);
  const dayOfEra = z - era * 146_097;
  const yearOfEra = Math.floor(
    (dayOfEra -
      Math.floor(dayOfEra / 1_460) +
      Math.floor(dayOfEra / 36_524) -
      Math.floor(dayOfEra / 146_096)) /
      365,
  );
  const year = yearOfEra + era * 400;
  const dayOfYear =
    dayOfEra -
    (365 * yearOfEra +
      Math.floor(yearOfEra / 4) -
      Math.floor(yearOfEra / 100));
  const marchMonth = Math.floor((5 * dayOfYear + 2) / 153);
  const day =
    dayOfYear - Math.floor((153 * marchMonth + 2) / 5) + 1;
  const month = marchMonth + (marchMonth &amp;#x3C; 10 ? 3 : -9);

  return {
    year: year + (month &amp;#x3C;= 2 ? 1 : 0),
    month,
    day,
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a clear, portable implementation with a broad year range. The bounded version trades that generality for speed over a fixed domain.&lt;/p&gt;
&lt;h2&gt;Recovering the Year with the Julian Map&lt;/h2&gt;
&lt;p&gt;The year-recovery stage uses Ben Joffe’s Julian map:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;q       = 4 * (epochDay + 719468) + 3
century = floor(q / 146097)
julian  = q + 3 * century + (century &amp;#x26; 3)
year    = floor(julian / 1461)
rem     = floor((julian - 1461 * year) / 4)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After correcting for the Gregorian 400-year cycle, the calculation temporarily maps the count into Julian four-year cycles. This shortens the dependency chain for recovering the year.&lt;/p&gt;
&lt;p&gt;The original Joffe expression, &lt;code&gt;q - (century &amp;#x26; ~3) + 4 * century&lt;/code&gt;, reduces to the shorter expression above by using &lt;code&gt;century = (century &amp;#x26; ~3) + (century &amp;#x26; 3)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Because &lt;code&gt;1461 = 4 * 365 + 1&lt;/code&gt;, &lt;code&gt;julian / 1461&lt;/code&gt; gives the March-based year, while the remaining value divided by four gives the day counted from March 1:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 &amp;#x3C;= rem &amp;#x3C;= 365
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;January and February are the final two months of this March-based year. Storing the &lt;code&gt;rem &gt;= 306&lt;/code&gt; year correction in bit 9 of the table removes the runtime comparison and addition.&lt;/p&gt;
&lt;h2&gt;Recovering Month and Day from a 366-Element Table&lt;/h2&gt;
&lt;p&gt;The arithmetic version recovers month and day with multiplication, shifts, division, and a January/February correction. Here, &lt;code&gt;rem&lt;/code&gt; has only 366 possible values, from &lt;code&gt;0&lt;/code&gt; through &lt;code&gt;365&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The corresponding year correction, month, and day are stored in a ten-bit integer:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(yearBump &amp;#x3C;&amp;#x3C; 9) | (month &amp;#x3C;&amp;#x3C; 5) | day
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The lookup and decoding are:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const packed = MONTH_DAY[dayOfMarchYear];
const year = marchYear + (packed &gt;&gt;&gt; 9);
const month = (packed &gt;&gt;&gt; 5) &amp;#x26; 15;
const day = packed &amp;#x26; 31;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The table occupies:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;366 * 2 = 732 bytes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is small enough for L1 cache on ordinary PCs and servers. An arithmetic implementation may still be preferable with a cold cache, on embedded systems, or when code and data size matter more than hot-loop throughput.&lt;/p&gt;
&lt;h2&gt;Replacing Division with Upward-Rounded Reciprocals in Bun&lt;/h2&gt;
&lt;p&gt;In Bun, multiplication by precomputed reciprocals was faster than &lt;code&gt;/146097&lt;/code&gt; and &lt;code&gt;/1461&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const INV_146097 = (1 / 146_097) * (1 + Number.EPSILON);
const INV_1461 = (1 / 1_461) * (1 + Number.EPSILON);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The resulting binary64 values are:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;INV_146097 = 0.0000068447675174712704
bits        = 0x3edcb5835e647c33

INV_1461   = 0.00068446269678302542
bits        = 0x3f466db072f2284e
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the ordinary nearest binary64 reciprocal, an exactly divisible input can produce a product just below the expected integer, causing &lt;code&gt;Math.floor&lt;/code&gt; to return one less. Multiplying by &lt;code&gt;1 + Number.EPSILON&lt;/code&gt; selects a neighboring value just above the exact reciprocal and avoids that failure.&lt;/p&gt;
&lt;p&gt;For both divisors:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;INV_d * d - 1 = 2^-52
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The maximum quotients in this domain are approximately 100 and 10,000, so the upward errors are at most approximately &lt;code&gt;2.3e-14&lt;/code&gt; and &lt;code&gt;2.3e-12&lt;/code&gt;. A nondivisible integer input remains at least &lt;code&gt;1/146097&lt;/code&gt; or &lt;code&gt;1/1461&lt;/code&gt; from the next quotient boundary. The upward error therefore cannot reach the next integer.&lt;/p&gt;
&lt;p&gt;This argument depends on the specified input range and IEEE-754 binary64 arithmetic. A wider range requires a new proof and exhaustive verification.&lt;/p&gt;
&lt;p&gt;Both products truncate to nonnegative integers below 2³¹ in this domain. The Bun implementation can therefore replace &lt;code&gt;Math.floor&lt;/code&gt; with 32-bit integer coercion using &lt;code&gt;| 0&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const century = (q * INV_146097) | 0;
const year = (julian * INV_1461) | 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The final implementation also folds the constant part of &lt;code&gt;q&lt;/code&gt; and keeps the shifts, product, and return value in the int32 domain:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const q = ((epochDay &amp;#x3C;&amp;#x3C; 2) + 2_877_875) | 0;
const remainder =
  (julian - Math.imul(year, 1_461)) | 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For nonnegative &lt;code&gt;century&lt;/code&gt; values:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;3c + (c &amp;#x26; 3) = 4 * (c - floor(c / 4))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Bun implementation consequently uses this equivalent form:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const julian =
  (q + ((century - (century &gt;&gt; 2)) &amp;#x3C;&amp;#x3C; 2)) | 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This form helped in the tested Bun environment. Optimizing C and Rust compilers can already lower the original expression to an equivalent instruction sequence, so it is not assumed to be universally faster.&lt;/p&gt;
&lt;h2&gt;Four-Wide SIMD Without an Additional Table&lt;/h2&gt;
&lt;p&gt;When several dates can be converted together, month/day recovery can be returned to arithmetic and evaluated across four NEON lanes instead of enlarging the lookup table.&lt;/p&gt;
&lt;p&gt;The January/February correction becomes one addition in the packed representation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;year += 1, month -= 12

packed += 512 - 12 * 32
        = 128
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The core can be expressed with Clang’s four-element vector type:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;typedef uint32_t u32x4 __attribute__((ext_vector_type(4)));

static inline u32x4 civil_from_days_4(u32x4 epoch_day) {
    const u32x4 q = epoch_day * 4u + 2877875u;
    const u32x4 century = q / 146097u;
    const u32x4 julian = q + century * 3u + (century &amp;#x26; 3u);
    const u32x4 year = julian / 1461u;
    const u32x4 rem = (julian - year * 1461u) &gt;&gt; 2;
    const u32x4 n = rem * 2141u + 197913u;
    const u32x4 march_month = n &gt;&gt; 16;
    const u32x4 day = (n &amp;#x26; 65535u) / 2141u + 1u;
    const u32x4 jan_feb = (u32x4)(rem &gt;= 306u);

    return year * 512u + march_month * 32u + day
        + (jan_feb &amp;#x26; 128u);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apple Clang with &lt;code&gt;-O3 -march=native&lt;/code&gt; lowered this function to NEON instructions. Processing the same randomized input array, with automatic vectorization disabled for the scalar side, produced:&lt;/p&gt;




















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Arithmetic version&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;ns/item&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;Relative throughput&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Scalar&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;2.694&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1.00x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Four-wide SIMD&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;0.713&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;3.78x&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The SIMD implementation matched Hinnant across all 3,652,059 epoch days. This is ARM64 batch throughput, not a like-for-like latency comparison with the one-item scalar API or packed-table implementation.&lt;/p&gt;
&lt;h2&gt;32-Bit Integer Bounds&lt;/h2&gt;
&lt;p&gt;Within the input domain:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;306 &amp;#x3C;= epochDay + 719468 &amp;#x3C;= 3652364
1227 &amp;#x3C;= q &amp;#x3C;= 14609459
0 &amp;#x3C;= century &amp;#x3C;= 99
0 &amp;#x3C;= julian &amp;#x3C; 14610000
0 &amp;#x3C;= year &amp;#x3C;= 9999
0 &amp;#x3C;= dayOfMarchYear &amp;#x3C;= 365
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The integer portion therefore fits comfortably in signed 32-bit values. Every &lt;code&gt;MONTH_DAY&lt;/code&gt; entry, including the year bump, fits in ten bits, and the complete table occupies 732 bytes.&lt;/p&gt;
&lt;h2&gt;Exhaustive Verification&lt;/h2&gt;
&lt;p&gt;Every epoch day in the specified range was compared with the Hinnant implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;2932896 - (-719162) + 1
= 3,652,059 inputs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The tested candidates included Hinnant, Neri–Schneider, Ben Joffe, the Bun upward-reciprocal variant, and the packed-table variant. The final TypeScript, Rust, and C implementations all matched throughout the complete range.&lt;/p&gt;
&lt;p&gt;The endpoints and Unix epoch were also checked directly:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;-719162 -&gt; 0001-01-01
       0 -&gt; 1970-01-01
 2932896 -&gt; 9999-12-31
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Benchmarks&lt;/h2&gt;
&lt;p&gt;The benchmarks ran on Apple Silicon ARM64. Each implementation processed a deterministically generated array of &lt;code&gt;2^20&lt;/code&gt; epoch days 24 times, rotating the input position between rounds. The best result from five trials was recorded.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Bun 1.4.0
rustc 1.96.0 -C opt-level=3 -C target-cpu=native
Apple clang 21.0.0 -O3 -march=native
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Fresh measurements produced the following results:&lt;/p&gt;

































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Environment&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;Usual Hinnant&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;Bounded version&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;Time reduction&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;Throughput&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Bun&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;37.088 ns/item&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;4.830 ns/item&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;87.0%&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;7.68x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Rust&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;5.734 ns/item&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1.500 ns/item&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;73.8%&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;3.82x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;C&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;5.315 ns/item&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1.603 ns/item&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;69.8%&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;3.32x&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;In Bun, the version with a table-stored year bump but remaining double arithmetic took 6.091 ns/item, while the int32-domain final version took 4.830 ns/item. In addition to storing the year bump, folding constants and using &lt;code&gt;Math.imul&lt;/code&gt; and shifts reduced transitions between double and int32 operations.&lt;/p&gt;
&lt;p&gt;Rust and C compilers already lower division by constants to multiplication and shifts where profitable. Leaving integer division in the source was therefore appropriate in those languages. Replacing the month/day arithmetic dependency chain with the packed lookup produced the larger remaining improvement.&lt;/p&gt;
&lt;p&gt;These measurements apply to one CPU, compiler/JIT version, and input distribution. Other Apple Silicon generations, x86-64, Node.js/V8, browsers, and different Rust or Clang versions should be measured independently.&lt;/p&gt;
&lt;h2&gt;Prior Work and Positioning&lt;/h2&gt;
&lt;p&gt;The year-recovery stage is based on Ben Joffe’s Julian map. Neri and Schneider systematically study Gregorian calendar conversion using multiplication, shifts, and Euclidean affine functions. Howard Hinnant’s implementation is the general reference baseline.&lt;/p&gt;
&lt;p&gt;The Julian map, year recovery, strength reduction of constant division, and lookup of month/day from day-of-year are therefore not claimed as individually new techniques.&lt;/p&gt;
&lt;p&gt;The construction presented here is a bounded specialization that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;restricts output years to &lt;code&gt;1..9999&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;recovers the year with Ben Joffe’s Julian map;&lt;/li&gt;
&lt;li&gt;maps the &lt;code&gt;0..365&lt;/code&gt; March-based day to a 732-byte packed table containing the year bump, month, and day;&lt;/li&gt;
&lt;li&gt;uses range-proven upward-rounded binary64 reciprocals in Bun; and&lt;/li&gt;
&lt;li&gt;leaves constant-division strength reduction to the C and Rust compilers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Among the public implementations examined, I did not find the same construction combining this fixed range, the Bun upward-rounded reciprocals, the Joffe mapping, and a packed month/day table. This does not establish optimality across all CPUs, languages, or input domains.&lt;/p&gt;
&lt;p&gt;A precise performance claim is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In these Apple Silicon benchmarks for Bun, Rust, and C, the bounded implementation was faster than the Hinnant, Neri–Schneider, and Ben Joffe implementations compared here.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Restricting the input to epoch days corresponding to years &lt;code&gt;1..9999&lt;/code&gt; enables the following &lt;code&gt;civil_from_days&lt;/code&gt; specialization:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;apply the Gregorian 400-year correction through the Julian map;&lt;/li&gt;
&lt;li&gt;recover the year and March-based day from a 1,461-day Julian cycle;&lt;/li&gt;
&lt;li&gt;recover the year bump, month, and day together from a 366-element packed table; and&lt;/li&gt;
&lt;li&gt;in Bun, replace two constant divisions with upward-rounded reciprocal multiplication.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In these Apple Silicon measurements, the bounded implementation reduced execution time by approximately 87% in Bun, 74% in Rust, and 70% in C compared with the usual Hinnant implementation. The table-free four-wide C SIMD arithmetic version additionally delivered approximately 3.78 times the batch throughput of its scalar arithmetic counterpart.&lt;/p&gt;
&lt;p&gt;For general-purpose code, Hinnant’s implementation remains the clear and broadly ranged choice. If a table is undesirable, the arithmetic Neri–Schneider or Joffe implementations are strong alternatives. This bounded version is intended for a measured hot path where the range guarantee and 732-byte table are acceptable.&lt;/p&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Howard Hinnant, &lt;a href=&quot;https://howardhinnant.github.io/date_algorithms.html&quot;&gt;chrono-Compatible Low-Level Date Algorithms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Cassio Neri and Lorenz Schneider, &lt;a href=&quot;https://arxiv.org/abs/2102.06959&quot;&gt;Euclidean Affine Functions and Applications to Calendar Algorithms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Cassio Neri, &lt;a href=&quot;https://github.com/cassioneri/eaf&quot;&gt;EAF supplementary implementation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Ben Joffe, &lt;a href=&quot;https://www.benjoffe.com/fast-date&quot;&gt;The Julian Map&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Ben Joffe, &lt;a href=&quot;https://www.benjoffe.com/fast-date-64&quot;&gt;A Very Fast 64-Bit Date Algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Ben Joffe, &lt;a href=&quot;https://github.com/benjoffe/fast-date-benchmarks&quot;&gt;fast-date-benchmarks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://veritycost.com/en/posts/fast-days-from-civil-41f45490/&quot;&gt;A Faster &lt;code&gt;days_from_civil&lt;/code&gt; Using Magic-Number Division&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>AWS Cost Optimization Techniques for Databricks on AWS Workloads</title><link>https://veritycost.com/en/posts/databricks-on-aws-aws-cost-reduction-tech-2bfd62baeddf/</link><guid isPermaLink="true">https://veritycost.com/en/posts/databricks-on-aws-aws-cost-reduction-tech-2bfd62baeddf/</guid><description>This guide focuses on practical AWS cost optimization strategies for scenarios where you deploy Databricks on your own AWS infrastructure…</description><pubDate>Mon, 27 Jan 2025 13:47:13 GMT</pubDate><content:encoded>&lt;p&gt;This guide focuses on practical AWS cost optimization strategies for scenarios where you deploy Databricks on your own AWS infrastructure, instead of leveraging a fully managed (serverless) Databricks solution. By applying these techniques, you can reduce AWS costs while maintaining efficient performance for Databricks workloads.&lt;/p&gt;
&lt;h2&gt;Reduce AWS Config Costs&lt;/h2&gt;
&lt;p&gt;A typical challenge with off-the-shelf solutions is the frequent spin-up and shutdown of EC2 instances. This activity generates a significant volume of inventory and change-tracking data in AWS Config, leading to higher costs.&lt;/p&gt;
&lt;p&gt;Over time, AWS has introduced additional managed rules for EC2 in AWS Config, which has steadily increased the cost per instance. (EC2 instances are often seen as flexible but somewhat “legacy” in AWS and are frequent targets for attacks, necessitating more rules.)&lt;/p&gt;
&lt;p&gt;Additionally, Config recording often triggers other AWS security services, creating a chain reaction of escalating costs.&lt;/p&gt;
&lt;p&gt;To manage these expenses, consider either excluding EC2 (or other resources) from AWS Config recording or switching from continuous recording to daily recording.&lt;/p&gt;
&lt;p&gt;However, keep in mind that this setting applies to all EC2 instances in the account. Applications or bastion hosts running on EC2 could unintentionally fall outside AWS Config’s monitoring, potentially increasing security risks.&lt;/p&gt;
&lt;h2&gt;Consider S3 Versioning&lt;/h2&gt;
&lt;p&gt;Delta tables include a time-travel feature, which may overlap with S3 versioning. Depending on your specific needs, you might find that S3 versioning is entirely unnecessary.&lt;/p&gt;
&lt;p&gt;If you decide to disable S3 versioning, keep in mind that older object versions will not be automatically removed. Be proactive in cleaning them up completely to avoid unnecessary storage costs.&lt;/p&gt;
&lt;p&gt;On the Delta table side, you can manage retention by removing data beyond its retention period using the VACUUM command.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Databricks recommends disabling bucket versioning so that the &lt;code&gt;VACUUM&lt;/code&gt; command can effectively remove unused data files.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Databricks recommends retaining three versions and implementing a lifecycle management policy that retains versions for 7 days or less for all S3 buckets with versioning enabled.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Use the S3 Storage Lens feature to analyze the size breakdown of your S3 buckets and gain insights into your storage usage. This tool also allows you to visualize the amount of data being used by S3 versioning, helping you identify potential areas for optimization.&lt;/p&gt;
&lt;h2&gt;Reduce EBS Costs&lt;/h2&gt;
&lt;p&gt;Start by migrating your EBS volumes from gp2 to gp3. This simple change can result in approximately 20% cost savings. For detailed configuration steps, refer to the following resources:&lt;/p&gt;
&lt;p&gt;Ideally, gp3 should be set as the default volume type.&lt;/p&gt;
&lt;p&gt;Another critical consideration is to verify whether any jobs are attaching a large number of EBS shuffle volumes. Most workloads don’t require additional shuffle volumes, as each worker already comes with 30GB + 150GB of EBS storage by default.&lt;/p&gt;
&lt;p&gt;Key settings to review include:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;aws_attributes.ebs_volume_type&lt;/code&gt;: Specifies whether shuffle volumes should be added, and if so, their type.
&lt;code&gt;aws_attributes.ebs_volume_size&lt;/code&gt;: Defines the size of the EBS volume (in GiB).
&lt;code&gt;aws_attributes.ebs_volume_count&lt;/code&gt;: Determines the number of EBS volumes.&lt;/p&gt;
&lt;p&gt;For instance, setting &lt;strong&gt;ebs_volume_count&lt;/strong&gt; to 2 effectively doubles your disk usage. To improve efficiency, you can also enable local storage autoscaling by activating &lt;strong&gt;enable_elastic_disk&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Reduce NAT Gateway Traffic&lt;/h2&gt;
&lt;p&gt;When configuring AWS environments — not just for Databricks — always set up an S3 Gateway Endpoint to ensure that S3 data transfers are cost-free. This is a best practice for minimizing unnecessary NAT Gateway traffic.&lt;/p&gt;
&lt;p&gt;For high-volume data transfers or specific destinations, consider setting up PrivateLink to further optimize costs.&lt;/p&gt;
&lt;h2&gt;Avoid Incremental Listing in Auto Loader&lt;/h2&gt;
&lt;p&gt;While the exact version is unclear, Incremental Listing in Auto Loader has been deprecated. By default, every 8th operation triggers a full directory scan using the S3 List API. This significantly increases API call charges as well as overall processing costs, including additional time and resource consumption.&lt;/p&gt;
&lt;h2&gt;Purchase Savings Plans&lt;/h2&gt;
&lt;p&gt;Consider purchasing Savings Plans to reduce EC2 costs. While EC2 Reserved Instances (RIs) typically offer greater discounts, Savings Plans are a better option if your workloads are not entirely predictable.&lt;/p&gt;
&lt;h2&gt;Leverage Spot Instances&lt;/h2&gt;
&lt;p&gt;Optimize your cluster costs by configuring the percentage of your cluster to use Spot Instances.&lt;/p&gt;
&lt;p&gt;Keep in mind, however, that the DBU cost remains unchanged when using Spot Instances. To achieve the best overall cost savings, including DBU and instance costs, start by reviewing the discount rates for the instance types you plan to use.&lt;/p&gt;
&lt;h2&gt;Consider Upgrading Instance Types&lt;/h2&gt;
&lt;p&gt;Upgrading your EC2 instance family can deliver better performance and potentially reduce costs. However, EC2 costs don’t always scale proportionately with DBU rates. In some cases, EC2 costs may remain unchanged while DBU rates increase. Since DBU costs heavily influence per-instance pricing, it’s crucial to monitor these changes closely.&lt;/p&gt;
&lt;p&gt;You may also consider exploring Graviton-based instances. However, be aware that several features are unavailable, as documented. Additionally, DBU rates aren’t always lower, and performance gains are not guaranteed, making thorough testing essential before deployment.&lt;/p&gt;
&lt;h2&gt;Additional Measures&lt;/h2&gt;
&lt;p&gt;Right-sizing your clusters is, of course, essential. You can optimize Databricks costs by tailoring configurations on a per-cluster basis. Be sure to review recommended practices step by step for maximum efficiency.&lt;/p&gt;
&lt;p&gt;For example, default cluster settings often incur additional costs due to idle DBU usage for up to two hours. Adjust these settings as needed to avoid unnecessary expenses.&lt;/p&gt;
&lt;p&gt;Upgrading your runtime version and adopting the latest features of Databricks and Delta Tables are highly recommended. These updates can improve performance and reduce costs.&lt;/p&gt;
&lt;p&gt;Additionally, don’t overlook improvements on the Spark side, as well as relevant updates from AWS, which can directly impact your setup’s efficiency and capabilities.&lt;/p&gt;
&lt;p&gt;For further optimization tips, check:&lt;/p&gt;
&lt;p&gt;Although not directly tied to AWS, third-party monitoring tools like Datadog may charge you for the EC2 resources used by Databricks.&lt;/p&gt;
&lt;p&gt;I won’t cover overall architecture or general performance optimization in this article.&lt;/p&gt;
&lt;h2&gt;Challenging Issues&lt;/h2&gt;
&lt;h3&gt;CloudTrail&lt;/h3&gt;
&lt;p&gt;Costs scale proportionally with the volume of service access.&lt;/p&gt;
&lt;h3&gt;GuardDuty&lt;/h3&gt;
&lt;p&gt;Enabling S3 Protection can lead to significant costs when analyzing Unity Catalog access on S3. You can review the cost breakdown in the GuardDuty usage section. Note that the underlying data source for this analysis is CloudTrail.&lt;/p&gt;
&lt;h2&gt;Finally&lt;/h2&gt;
&lt;p&gt;Cost optimization can feel overwhelming, but starting small can lead to big results. Begin by focusing on AWS Config settings — adjust recording intervals or exclude unnecessary resources to immediately reduce costs while maintaining essential monitoring. Small changes like these can pave the way for larger, more comprehensive optimizations down the line. Let’s take the first step today!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This article is the English version of the original article written in Japanese, available &lt;a href=&quot;https://veritycost.com/posts/databricks-on-aws-aws-cost-reduction-tech-2d6ef4510c60/&quot;&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>Databricks on AWS上のAWSコストを削減する技術</title><link>https://veritycost.com/posts/databricks-on-aws-aws-cost-reduction-tech-2d6ef4510c60/</link><guid isPermaLink="true">https://veritycost.com/posts/databricks-on-aws-aws-cost-reduction-tech-2d6ef4510c60/</guid><description>サーバーレス(マネージド)ではなく自組織のAWS上で運用した場合です。</description><pubDate>Fri, 19 Jan 2024 03:26:32 GMT</pubDate><content:encoded>&lt;p&gt;サーバーレス(マネージド)ではなく自組織のAWS上で運用した場合です。&lt;/p&gt;
&lt;h2&gt;AWS Config代を削減する&lt;/h2&gt;
&lt;p&gt;既製品でよくある仕組みでEC2が起動終了を大量に繰り返します。それに応じて、リソースのインベントリと変更の追跡をすべくAWS Configが大量のデータを取得し料金が膨れ上がります。&lt;/p&gt;
&lt;p&gt;年々AWS ConfigのEC2周りマネージドルールが増えており、EC2 1台あたりのConfigコストも増加傾向です。&lt;br&gt;＊EC2は攻撃の対象になりがち、AWSとしても古い仕組み、ある意味なんでもできるということでルールが増えていく模様
加えて、Configデータを起点にセキュリティ系AWSサービスのコストが連鎖して上昇します。&lt;/p&gt;
&lt;p&gt;対策としてはAWS ConfigでEC2等の記録除外設定を入れるか、即時記録ではなく定期的な記録に変更するかのどちらかです。&lt;/p&gt;
&lt;p&gt;一方、EC2すべてに設定することから、EC2上のアプリケーションや踏み台サーバが同一アカウントにあると、AWS Configで追跡できずリスクが高まります。&lt;/p&gt;
&lt;h2&gt;S3にあるデータの保持期間を検討する&lt;/h2&gt;
&lt;p&gt;Deltaテーブルにはタイムトラベル機能があり、S3のバージョニングと競合しています。要件によりますがS3側のバージョニングは不要ではないでしょうか。&lt;/p&gt;
&lt;p&gt;S3のバージョニングを停止してもオブジェクトの古いバージョンは残ったままです。全部削除しましょう。&lt;/p&gt;
&lt;p&gt;Deltaテーブル側ではVACUUM処理により保持期間が切れたものを削除できます。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Databricks では、&lt;code&gt;*VACUUM*&lt;/code&gt; コマンドで未使用のデータ ファイルを効果的に削除できるように、バケットのバージョン管理を無効にすることをお勧めします。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;EBS代を削減する&lt;/h2&gt;
&lt;p&gt;まずEBSをgp2からgp3に変更しましょう。20%程度のコスト削減が見込めます。設定方法は以下のリンクより。&lt;/p&gt;
&lt;p&gt;初期設定をgp3にしてもらえると嬉しいですね。&lt;/p&gt;
&lt;p&gt;次に重要な点として、ジョブ等に大量のEBS シャッフルボリュームがアタッチされていることがあるので確認しましょう。多くのワークロードでは追加のシャッフルボリュームはいらないはず。そもそも初期設定でワーカーに30GB+150GBのEBSが準備されています。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;aws_attributes.ebs_volume_type&lt;/code&gt; シャッフルボリュームを足すかどうか、
および足す場合の種類
&lt;code&gt;aws_attributes.ebs_volume_size&lt;/code&gt; AWS EBS ボリュームのサイズ (GiB 単位)
&lt;code&gt;aws_attributes.ebs_volume_count&lt;/code&gt; AWS EBS ボリュームの数&lt;/p&gt;
&lt;p&gt;見ての通り&lt;code&gt;ebs_volume_count&lt;/code&gt; を2にすれば2倍ディスクを消費します。enable_elastic_diskでローカルストレージのautoscalingも可能です。&lt;/p&gt;
&lt;h2&gt;NAT Gatewayの通信を抑える&lt;/h2&gt;
&lt;p&gt;必ずS3 Gateway Endpointを設定し、S3との通信料金を無料にしましょう。&lt;/p&gt;
&lt;p&gt;通信先とデータ量次第で、PrivateLinkを設定しましょう。&lt;/p&gt;
&lt;h2&gt;Savings Planを購入する&lt;/h2&gt;
&lt;p&gt;Savings Planを買ってEC2代を節約しましょう。&lt;/p&gt;
&lt;p&gt;EC2 RIのほうが安い傾向にあるものの、ワークロードが完全に読めていない限りはSavings Planが確実です。&lt;/p&gt;
&lt;h2&gt;Spot Instanceを活用する&lt;/h2&gt;
&lt;p&gt;Spot Instance割合が設定できます。&lt;/p&gt;
&lt;p&gt;ただしDBUは変わらないようです。&lt;/p&gt;
&lt;p&gt;どのインスタンスを使うか決めるパラメータとしては以下が考えられます。&lt;br&gt;&lt;em&gt;(インスタンスDBU&lt;/em&gt; + インスタンス&lt;em&gt;単価** ( &lt;em&gt;Spot Instance割合&lt;/em&gt;&lt;/em&gt; ( &lt;em&gt;該当インスタンスRI割引後価格&lt;/em&gt;) + (1 — &lt;em&gt;Spot Instance割合&lt;/em&gt;)* &lt;em&gt;SavingsPlan適用時価格&lt;/em&gt;) + *EBS代)** _該当インスタンスでの実行時間 _ 該当インスタンスの数*&lt;/p&gt;
&lt;p&gt;なかなか複雑です。まずは利用したいインスタンスの割引率を見てみましょう。&lt;/p&gt;
&lt;h2&gt;インスタンスの最新化を検討する&lt;/h2&gt;
&lt;p&gt;EC2のファミリをアップグレードすればIntelなら第6世代まではコストを抑えつつ性能が上がります。&lt;/p&gt;
&lt;p&gt;ただし、EC2インスタンスの料金とDBUが必ずしも比例しているわけではない模様。確認したところEC2料金はそのままでもDBUが上昇することがあります。DBUのほうがインスタンスあたり単価が大きいので注意。&lt;/p&gt;
&lt;p&gt;Graviton系インスタンスも利用可能です。残念ながらドキュメントにある通り、複数の機能が利用できません。またDBUも安いということはなさそうで、パフォーマンスは向上するとは限らず検証が必要です。&lt;/p&gt;
&lt;h2&gt;その他対応事項&lt;/h2&gt;
&lt;p&gt;当然ですがクラスターのサイズを調整しましょう。Databricks料金ごと最適化できます。以下のリンクは推奨事項です。一つずつ確認しましょう。例えばクラスタの初期設定ではDBU含めアイドル2時間分追加コストがかかります。&lt;/p&gt;
&lt;p&gt;Runtimeのバージョンを上げつつ、DatabricksやDelta Tableの最新機能に移行していきましょう。Spark側の進化も忘れずに。AWSもたまに関係ある良いアップデートがあります。&lt;/p&gt;
&lt;p&gt;最適化はこちら。このページにもある程度記載されつつ、サイドバーやページ内リンクからさらに詳細ページに飛べます。&lt;/p&gt;
&lt;p&gt;下記の記事は細かいtipsが載っていておすすめです&lt;/p&gt;
&lt;p&gt;AWSと直接関係ないですが、Datadogなど3rd Partyモニタリングツールは確認しないとDatabricksのEC2分の料金が請求されます。&lt;/p&gt;
&lt;p&gt;全体のアーキテクチャ、一般的なパフォーマンス最適化といった話は割愛します。&lt;/p&gt;
&lt;h2&gt;対応が難しいもの&lt;/h2&gt;
&lt;h3&gt;CloudTrail&lt;/h3&gt;
&lt;p&gt;基本的には各種サービスのアクセスと比例します。&lt;/p&gt;
&lt;h3&gt;GuardDuty&lt;/h3&gt;
&lt;p&gt;S3 Protectionを有効にしていると、S3上のUnity Catalogのアクセス解析代が多くかかります。バケット内訳はGuardDutyの使用状況欄から確認できます。&lt;/p&gt;
&lt;p&gt;解析の元ネタはCloudTrailです。&lt;/p&gt;
&lt;h2&gt;最後に&lt;/h2&gt;
&lt;p&gt;DatabricksでかかるAWS代を節約してDatabricksのDBUをたくさん使いましょう（？）&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>データセンターは公害か</title><link>https://veritycost.com/posts/datacenter-pollution-8e3b4685/</link><guid isPermaLink="true">https://veritycost.com/posts/datacenter-pollution-8e3b4685/</guid><description>データセンターの環境負荷が注目されている。電力消費・水使用・廃熱・廃棄物の観点から、それを「公害」と呼べるのか整理する。</description><pubDate>Sat, 02 May 2026 00:12:20 GMT</pubDate><content:encoded>&lt;p&gt;データセンター(DC)が話題になることが増えました。しかしギャップがあるようなので情報を整理しました。&lt;/p&gt;
&lt;h2&gt;特に問題となる設備&lt;/h2&gt;
&lt;p&gt;データセンターにはサーバだけあるわけではありません。冷却設備と電源、非常用含めたディーゼル発電機が問題になりやすいです。&lt;/p&gt;
&lt;h2&gt;騒音/低周波/振動&lt;/h2&gt;
&lt;p&gt;サーバというより冷却設備と電源設備の騒音です。低周波は外部に長くいないと影響を感じにくいでしょう。&lt;/p&gt;
&lt;h2&gt;排熱&lt;/h2&gt;
&lt;p&gt;特に夏場は影響が無視できなくなります。温暖化やヒートアイランドに埋もれがちですが、局所的には負荷を増やす方向に働きます。&lt;/p&gt;
&lt;h2&gt;臭いと危険なガス&lt;/h2&gt;
&lt;p&gt;主に非常用ディーゼル発電機から発生します。ここでポイントとなるのが、定期試験運転は法令や保守要件により不可避であり、月次・年次で実施されることです。短時間であってもイベント性が高く、騒音・排ガス・臭気として住民に知覚されやすい点が特徴です。消防法で求められた負荷試験にもなればさらに負荷をかけて動かすことになります。&lt;/p&gt;
&lt;p&gt;(消火用のガスもありますがこれは他の建物でも変わらないので除外)&lt;/p&gt;
&lt;h2&gt;雇用&lt;/h2&gt;
&lt;p&gt;立ち上げ期はともかく、運用は少人数です。地域によくある誤解として、Googleのソフトウェアエンジニアのような良質な雇用が多数集まるという期待があります。実際は契約業者に作業を任せますし、人数も少なく、そもそも人員はソフトウェアエンジニアではありません。効果は限定的です。&lt;/p&gt;
&lt;h2&gt;不透明&lt;/h2&gt;
&lt;p&gt;セキュリティ上の必然もありNDAが多くなります。しかし住民から見ると説明がない施設になるでしょう。&lt;/p&gt;
&lt;h2&gt;日本でのデータセンター建設の地域メリット&lt;/h2&gt;
&lt;p&gt;日本の場合固定資産税による税収アップは間違いないです。日本では基本的に誘致のために税金を今のところ使ってないので黒字でしょう。印西市の事例がわかりやすいですね。&lt;/p&gt;
&lt;h2&gt;あまり日本では問題にならなそうな項目&lt;/h2&gt;
&lt;p&gt;海外では議論になっても日本ではそこまでではないという項目もあります。&lt;/p&gt;
&lt;p&gt;現時点では、日本の電力系統は比較的安定しています。また電気代が変動制ではないので住民が電気代増加に巻き込まれることもないでしょう。※ただし、DC密集地での「変電所容量の奪い合い」という産業的な制約は出始めています&lt;/p&gt;
&lt;p&gt;空冷式が多く、水利用は相対的に少ない傾向があります。高湿度環境では蒸発冷却の効率が出にくいことも一因と考えられます。&lt;/p&gt;
&lt;p&gt;DC誘致のために税金を使う底辺への競争も発生していません。&lt;/p&gt;
&lt;h2&gt;なぜ一部のオンプレエンジニアは反対運動に理解を示さないのか&lt;/h2&gt;
&lt;h3&gt;仕事場への認識&lt;/h3&gt;
&lt;p&gt;住民から見れば、巨大な電力消費施設・排熱施設・非常用発電機施設ですが、エンジニアから見ると「ラックがある場所」です。視野がラック内・ケーブル・手順書・入館申請に閉じがち。日本のオンプレエンジニアはデータセンターを「入館する場所」として見ていて、住民は「押し寄せてくる産業施設」として見ている。&lt;/p&gt;
&lt;p&gt;データセンター特有の音、温度、湿度も慣れてしまえば背景に興味はなくなるでしょう。&lt;/p&gt;
&lt;h3&gt;時間の流れの非対称性&lt;/h3&gt;
&lt;p&gt;基本的になにかあるとき、日中に訪問するだけの人が多いので、24時間365日向き合う住民感覚とはずれるでしょう。データセンターは24時間稼働です。たまにいくだけなら問題を感じにくい。&lt;/p&gt;
&lt;h3&gt;職能ギャップ&lt;/h3&gt;
&lt;p&gt;サーバやラックに詳しくてもファシリティは専門外です。建物の建設、設計や保守をしているわけではないので。そしてファシリティを担当する専門家は特定企業にいるごく少数でしょう。レイヤーが異なるので触る機会も見る機会もありません。&lt;/p&gt;
&lt;p&gt;コロケーション型としてよくある、DCのラックやスペースを借りるだけのタイプならなおさら無関係です。電源・空調・発電機といった設備は完全に外部化され、問題認識が生まれにくい構造になります。&lt;/p&gt;
&lt;h3&gt;都市型DCの特殊性&lt;/h3&gt;
&lt;p&gt;大手町にあるような都心型データセンターは地下にあります。騒音振動などは減衰され、景観は関係なく、排熱も都心に紛れて気づけません。そのため都市型しか知らなければ反対への理解は難しいでしょう。&lt;/p&gt;
&lt;h2&gt;結論&lt;/h2&gt;
&lt;p&gt;海外よりは問題が発生しづらいのは幸いですが、それでも問題は必ず発生します。押し付けではない本物の住民理解やコミュニケーションが必要かと思います。&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>Datadogコスト効率化プロダクトを作ってみて仮説検証して考えたこと</title><link>https://veritycost.com/posts/datadog-cost-optimization-product-41d2fde7b5ce/</link><guid isPermaLink="true">https://veritycost.com/posts/datadog-cost-optimization-product-41d2fde7b5ce/</guid><description>当初の仮説 AWSのFinOps領域は競合が多く、ツールも成熟している。一方で、Datadogはクラウドプロバイダに次ぐ支出規模（コスト2位）になることが多いにもかかわらず、基本的なコスト削減が十分に行われていないケースが多く、競合も少ないと考えた。</description><pubDate>Mon, 06 Oct 2025 01:21:00 GMT</pubDate><content:encoded>&lt;h2&gt;当初の仮説&lt;/h2&gt;
&lt;p&gt;AWSのFinOps領域は競合が多く、ツールも成熟している。一方で、Datadogはクラウドプロバイダに次ぐ支出規模（コスト2位）になることが多いにもかかわらず、基本的なコスト削減が十分に行われていないケースが多く、競合も少ないと考えた。&lt;/p&gt;
&lt;p&gt;これまでDatadogのコスト削減を実践してきたが、海外を含めても、（手前味噌ながら）下記のレベルに到達している公開情報は見当たらず、差別化の余地があると判断した。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://veritycost.com/posts/datadog-cost-reduction-tips-378b319c87d6/&quot;&gt;Datadogの支払い/コストを削減するtips集&lt;/a&gt; (veritycost.com) - AWSといったクラウドプロバイダへの支払いを100とすると、Observabilityベンダーとの支払いは、おおよそ15〜30程度となります。&lt;/p&gt;
&lt;p&gt;また、コスト削減のようなボトムライン改善型サービスは、ユーザーとサービス提供者の間で利益相反が起きやすいものの、マイクロSaaSとしてニッチ市場で収益を確保する分には問題ないと考えた。＊1&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/datadog-cost-optimization-product-41d2fde7b5ce.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;仮説が外れた要因&lt;/h2&gt;
&lt;h3&gt;コストへの無関心&lt;/h3&gt;
&lt;p&gt;そもそも多くのエンジニアやエンジニア組織は、根本的にコストに関心がない。&lt;/p&gt;
&lt;p&gt;コスト削減やコストコントロールは「自分の仕事ではない」と捉えられており、興味を持たれにくい。&lt;/p&gt;
&lt;p&gt;仮に関心があったとしても、AWSコストの管理だけで手一杯である。&lt;/p&gt;
&lt;p&gt;さらに、Datadogのコストまで意識が向く層でも、比較基準が乏しく、しばしばダニング＝クルーガー効果が働く可能性がある。&lt;/p&gt;
&lt;p&gt;ここまで突破できたとしても、大きめの組織では、実際に担当しているのは現場エンジニア一人だけというケースが多く、購買の意思決定者からは遠く離れている。&lt;/p&gt;
&lt;p&gt;そのため、たとえ予算枠内に収まる安価なツールであっても、導入判断まで至るのは難しい。&lt;/p&gt;
&lt;h3&gt;コスト可視化機能の登場&lt;/h3&gt;
&lt;p&gt;Datadog自身もDatadogコスト可視化機能を提供し始めており、「少し見えれば十分」と考える大多数の利用者層にはそれで満足されてしまう。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.datadoghq.com/product/cloud-cost-management/&quot;&gt;Cloud Cost Management | Datadog&lt;/a&gt; (&lt;a href=&quot;http://www.datadoghq.com&quot;&gt;www.datadoghq.com&lt;/a&gt;) - Unify cost and performance data to empower engineers to optimize workloads and enable FinOps to reduce waste and drive…&lt;/p&gt;
&lt;h3&gt;APIキーのハードル&lt;/h3&gt;
&lt;p&gt;Datadogをプラットフォームとして考えると、実質的にはOIDCが使えず、権限の強いAPIキーしか3rd Partyツールと連携できない。&lt;/p&gt;
&lt;p&gt;セキュリティ上の不安が残るのは当然で、これでは試してもらうこと自体が難しい。&lt;/p&gt;
&lt;h3&gt;結論&lt;/h3&gt;
&lt;p&gt;総合して考えると、プロダクトを導入してもらうには、現状（＝「何もしない」）との間に圧倒的な差、すなわち10倍差が必要だった。&lt;/p&gt;
&lt;p&gt;しかし、それを満たすだけの価値、認知・導入・運用コストを含めて既存行動を変えるに足る圧倒的な便益を提供できなかったということになる。&lt;/p&gt;
&lt;h2&gt;学び&lt;/h2&gt;
&lt;p&gt;プロダクト開発という点では、ほぼ100％ vibe codingでの開発や、Rust言語・ GenAI APIを組み込んだプロダクト開発の勘を習得できた（検証前に開発するのは良くないが、それはそれとして開発したかったので）。＊2&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/yoshi-taka/dd-cost-optimizer&quot;&gt;GitHub - yoshi-taka/dd-cost-optimizer&lt;/a&gt; (github.com) - Contribute to yoshi-taka/dd-cost-optimizer development by creating an account on GitHub.&lt;/p&gt;
&lt;p&gt;また、顧客候補の探し方などマーケティング面での経験を積むことができ、結果的には「得した」と言える。&lt;/p&gt;
&lt;p&gt;＊1 特に成果報酬型のコスト削減は悪夢&lt;br&gt;＊2 AIは&lt;a href=&quot;https://nautil.us/will-ai-harm-us-better-to-ask-how-well-reckon-with-our-hybrid-nature-236098/&quot;&gt;競合的技術&lt;/a&gt;である。いくら電卓で計算しても暗算が速くならないのと同じであり、指示されたことをただやるだけの人間が全く学ばないのと等しい。AIからの提案について自分でひと手間かけない限り成長せず、人体実験の被験者や服従者の気持ちになることができる。今回はAIが得意とする「言語の構文周り」に関してはあえてそれで挑戦し、言語周辺の開発インフラについては徹底的に調べて最適化した。アーキテクチャや非機能要件についても本番レベルで対応している&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>Datadogの支払い/コストを削減するtips集</title><link>https://veritycost.com/posts/datadog-cost-reduction-tips-378b319c87d6/</link><guid isPermaLink="true">https://veritycost.com/posts/datadog-cost-reduction-tips-378b319c87d6/</guid><description>AWSといったクラウドプロバイダへの支払いを100とすると、Observabilityベンダーとの支払いは、おおよそ15〜30程度となります。</description><pubDate>Mon, 12 Jun 2023 06:02:55 GMT</pubDate><content:encoded>&lt;p&gt;AWSといったクラウドプロバイダへの支払いを100とすると、Observabilityベンダーとの支払いは、おおよそ15〜30程度となります。&lt;/p&gt;
&lt;p&gt;この記事では大まかな方針を記載し、削減余地が大きいログ、APM、Infra Hostについては詳述します。&lt;/p&gt;
&lt;h2&gt;金額と明細を可視化する&lt;/h2&gt;
&lt;p&gt;Admin以外の権限のユーザーだと利用量は参照できますが、具体的な金額が見えません。Standard RoleなどにDatadogのBillingを参照できる権限をつけるとよいでしょう。まずは金額と明細をメンバーに共有しましょう。&lt;/p&gt;
&lt;h2&gt;Usage Attributionで明細を可視化する&lt;/h2&gt;
&lt;figure class=&quot;media-figure&quot;&gt;
  ![](../../assets/datadog-cost-reduction-tips-378b319c87d6-01.png)
  &lt;figcaption&gt;
    [https://docs.datadoghq.com/ja/account_management/billing/usage_attribution/](https://docs.datadoghq.com/ja/account_management/billing/usage_attribution/)
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.datadoghq.com/ja/account_management/billing/usage_attribution/&quot;&gt;契約プラン次第&lt;/a&gt;ですが各チームに自分で調査してもらうのに便利です。&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;ログを削減する&lt;/strong&gt;&lt;/h2&gt;
&lt;h3&gt;LambdaのSTART / ENDログを転送抑制する&lt;/h3&gt;
&lt;p&gt;Lambdaでは必ずSTART / ENDログが出力されますが、このログは情報量が少ないです。特にトレースを導入しているなら不要でしょう。除外するとログが半分以下になることも。&lt;/p&gt;
&lt;p&gt;設定方法は以下の通り。転送ルートがLambda Extensions経由でもDatadog Forwarder経由でも有益です。&lt;/p&gt;
&lt;p&gt;REPORTログはlambda enhanced metricsとしてduration計算などに使われます。一方、送信しingestとして取り込みさえできればログindexには不要です。indexから除外も検討しましょう&lt;/p&gt;
&lt;h3&gt;どのログが多いのかログ画面のTree Mapで可視化する&lt;/h3&gt;
&lt;figure class=&quot;media-figure&quot;&gt;
  ![](../../assets/datadog-cost-reduction-tips-378b319c87d6-02.png)
  &lt;figcaption&gt;
    [https://docs.datadoghq.com/ja/dashboards/widgets/treemap/](https://docs.datadoghq.com/ja/dashboards/widgets/treemap/)
    画像はイメージです
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;ログ画面でTree Mapを使い、カテゴリごとに量を可視化しましょう。同時に複数のカテゴリでグルーピングできます。アカウント、サービス、Hostあたりが便利です。&lt;/p&gt;
&lt;h3&gt;どのログが多いのかメトリクスから探る&lt;/h3&gt;
&lt;figure class=&quot;media-figure&quot;&gt;
  ![](../../assets/datadog-cost-reduction-tips-378b319c87d6-03.png)
  &lt;figcaption&gt;
    [https://docs.datadoghq.com/ja/dashboards/querying/](https://docs.datadoghq.com/ja/dashboards/querying/)
    画像はイメージです
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;メトリクスを使うなら、datadog.estimated_usage.logs.ingested_bytesからおおよそ可視化できます。&lt;/p&gt;
&lt;p&gt;メトリクスの強みは&lt;a href=&quot;https://docs.datadoghq.com/ja/dashboards/querying/&quot;&gt;Split Graph機能&lt;/a&gt;です。グラフをカテゴリで分割できるので見やすく助かります。&lt;/p&gt;
&lt;h3&gt;ログPatterns機能で不要なログパターンを探る&lt;/h3&gt;
&lt;figure class=&quot;media-figure&quot;&gt;
  ![](../../assets/datadog-cost-reduction-tips-378b319c87d6-04.png)
  &lt;figcaption&gt;
    [https://docs.datadoghq.com/ja/logs/explorer/analytics/patterns/](https://docs.datadoghq.com/ja/logs/explorer/analytics/patterns/)
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Patterns機能は過小評価されています。不要なパターンのログ、おかしいログが出てないか、コスト削減の観点では量が多いものを探してみましょう。&lt;/p&gt;
&lt;h3&gt;Statusで絞る&lt;/h3&gt;
&lt;p&gt;WARN、ERRORといったログが大量に出ているのは良くない傾向です。整理整頓しましょう。アプリケーションも改善できる可能性があります。&lt;/p&gt;
&lt;h3&gt;二重出力を確認する&lt;/h3&gt;
&lt;p&gt;たまに二重に出力されています。Datadogの設定ミスの可能性が高いです。&lt;/p&gt;
&lt;h3&gt;サンプリングする&lt;/h3&gt;
&lt;p&gt;代わり映えのないログはサンプリングを検討しましょう&lt;/p&gt;
&lt;p&gt;転送量を削減すれば、AWS内ならDatadog ForwarderのLambda実行コスト、転送コストも削減可能です。Datadog ForwarderのLambdaとしてのコストは地味に嵩みます。&lt;/p&gt;
&lt;h2&gt;APMを削減する&lt;/h2&gt;
&lt;h3&gt;可視化する&lt;/h3&gt;
&lt;p&gt;方法はログと同じです。上記を参照ください。&lt;/p&gt;
&lt;h3&gt;プロキシ的なサービスを除外する&lt;/h3&gt;
&lt;p&gt;安定しているサービスはAPMの対象から除外すると良いでしょう。&lt;/p&gt;
&lt;h3&gt;サンプリングする&lt;/h3&gt;
&lt;p&gt;トレース全体をサンプリングする方法と、トレース内の特定のアクセスだけをサンプリングする方法があります。DD_TRACE_SAMPLING_RULESにより、例えばDBアクセスだけをサンプリングすることが可能です。&lt;/p&gt;
&lt;h3&gt;二重出力を確認する&lt;/h3&gt;
&lt;p&gt;たまに二重に出力されています。Datadogの設定ミスの可能性が高いです。&lt;/p&gt;
&lt;h2&gt;Infra Hostを削減する&lt;/h2&gt;
&lt;h3&gt;AWS Integrationから除外する&lt;/h3&gt;
&lt;p&gt;Datadog Agentを入れていないEC2ホストもAWS Integrationを設定していると課金対象となります。EC2全部について監視不要ならAWS Integration画面からEC2連携を除外しましょう。&lt;/p&gt;
&lt;p&gt;CloudWatch Metric Streamsを設定しているときは追加作業が必要です。AWS側で名前空間からEC2を除外し、Datadogに流れないようにしましょう。&lt;/p&gt;
&lt;h2&gt;その他利用量の削減&lt;/h2&gt;
&lt;h3&gt;API テストを減らす&lt;/h3&gt;
&lt;p&gt;実行回数で課金されるため、1分に1回のチェックだと1時間に1回のチェックに比べ60倍の金額となります。開発環境なら長くても良いでしょう。ワークロードに応じて間隔を空けましょう。&lt;/p&gt;
&lt;p&gt;ロケーション数とも金額が比例するため、東京だけに絞るのも一つの方法です。&lt;/p&gt;
&lt;p&gt;他のDatadog機能と連携をしないなら別のサービスに切り替えたほうが安いことも。&lt;/p&gt;
&lt;p&gt;時間帯によって間隔を変更できると便利ですが、標準機能には今のところ存在しないため、行うならDatadog APIで随時設定する仕組みを準備しましょう。&lt;/p&gt;
&lt;h3&gt;Sensitive Scannerを削減する&lt;/h3&gt;
&lt;p&gt;Lambdaの標準ログはSensitiveデータが入りようがないのでSTART / END / REPORTログを除外しましょう。&lt;/p&gt;
&lt;h2&gt;条件の変更&lt;/h2&gt;
&lt;p&gt;カード払いから領収書払いに切り替えることもできます。有利かどうかは経理部門やファイナンス部門に確認しましょう。&lt;/p&gt;
&lt;p&gt;コミットメントや各種プランも適宜見直しましょう。&lt;/p&gt;
&lt;h2&gt;最後に&lt;/h2&gt;
&lt;p&gt;以下の記事でも述べた通り、特にログの部分はAWSコストと連動しています。一石二鳥を狙うとよいです。&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>How Busy Engineers Can Reduce Datadog Costs with Actionable Tips</title><link>https://veritycost.com/en/posts/datadog-cost-reduction-tips-c2e34f0bba8b/</link><guid isPermaLink="true">https://veritycost.com/en/posts/datadog-cost-reduction-tips-c2e34f0bba8b/</guid><description>Feeling the pinch of rising Datadog expenses? If you&apos;re searching for effective Datadog cost optimization strategies, this guide is for you.</description><pubDate>Mon, 20 Jan 2025 13:29:43 GMT</pubDate><content:encoded>&lt;p&gt;Feeling the pinch of rising Datadog expenses?&lt;/p&gt;
&lt;p&gt;If you’re searching for effective Datadog cost optimization strategies, this guide is for you. It provides actionable tips to reduce Datadog costs by focusing on high-impact areas such as log management, APM (Application Performance Monitoring), and infrastructure hosts. By implementing these techniques, you can significantly cut Datadog expenses without sacrificing visibility or performance.&lt;/p&gt;
&lt;h2&gt;Start by Making Costs and Details Transparent Across Your Organization&lt;/h2&gt;
&lt;p&gt;I know you are busy — bring others on board to drive cost-saving initiatives.&lt;/p&gt;
&lt;p&gt;Any roles except the Datadog Admin role can view usage but don’t have access to detailed cost data.** **Consider granting roles like Standard Role the ability to access Datadog billing details. Start by sharing the cost breakdown and insights with your teams.&lt;/p&gt;
&lt;p&gt;You can request access to more detailed reports.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Further detailed usage reports can also be created on request by your account representative.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;&lt;strong&gt;Use Usage Attribution for Detailed Insights&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;If you’re on an Enterprise plan, this feature is a powerful tool for enabling teams to investigate their own usage patterns independently.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Optimize Log Volume&lt;/strong&gt;&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;Suppress Lambda START/END Logs&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Lambda automatically generates START/END logs, but they provide minimal value. If you’re already using tracing, these logs are likely redundant. Excluding them can significantly reduce log volume, sometimes by more than half. This is effective whether you’re using Lambda Extensions or Datadog Forwarder for log transfer.&lt;/p&gt;
&lt;p&gt;By suppressing logs on the client side, you can reduce subsequent ingestion processes downstream.&lt;/p&gt;
&lt;h3&gt;Avoid Indexing REPORT Logs&lt;/h3&gt;
&lt;p&gt;Lambda REPORT logs support enhanced metrics like duration calculations. However, once they’re ingested, they don’t need to be indexed. Consider excluding them from the log index to save costs, and also exclude them from the Sensitive Data Scanner for the same reason.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Use Tree Map Visualization to Analyze Log Volume&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Leverage the Tree Map feature on the log screen to analyze log volume by category. You can group logs by multiple dimensions simultaneously, such as account, service, or host, for better insights.&lt;/p&gt;
&lt;figure class=&quot;media-figure&quot;&gt;
  ![](../../assets/datadog-cost-reduction-tips-c2e34f0bba8b-01.png)
  &lt;figcaption&gt;
    [https://docs.datadoghq.com/dashboards/widgets/treemap/](https://docs.datadoghq.com/dashboards/widgets/treemap/)
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Explore Log Volume Using Metrics&lt;/strong&gt;&lt;/p&gt;
&lt;figure class=&quot;media-figure&quot;&gt;
  ![](../../assets/datadog-cost-reduction-tips-c2e34f0bba8b-02.png)
  &lt;figcaption&gt;
    [https://docs.datadoghq.com/dashboards/querying/](https://docs.datadoghq.com/dashboards/querying/)
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If you prefer metrics, the &lt;em&gt;datadog.estimated_usage.logs.ingested_bytes&lt;/em&gt; metric can provide a clear visualization of log volume. A standout feature is &lt;a href=&quot;https://docs.datadoghq.com/dashboards/querying/#split-graph&quot;&gt;Split Graphs&lt;/a&gt;, which allows you to break down graphs by category for better clarity and actionable insights.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Identify Unnecessary Log Patterns Using the Patterns Feature&lt;/strong&gt;&lt;/h3&gt;
&lt;figure class=&quot;media-figure&quot;&gt;
  ![](../../assets/datadog-cost-reduction-tips-c2e34f0bba8b-03.png)
  &lt;figcaption&gt;
    [https://docs.datadoghq.com/logs/explorer/analytics/patterns/](https://docs.datadoghq.com/logs/explorer/analytics/patterns/)
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The Patterns feature is often overlooked. Leverage it to pinpoint unnecessary or anomalous log patterns. To optimize costs, prioritize addressing high-volume logs that provide little value.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Filter Logs by Status&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;A high volume of WARN or ERROR logs is a red flag. Address these logs to clean up your system and potentially identify opportunities for application improvements.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Check for Duplicate Log Outputs&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Occasionally, logs are outputted twice. This is often due to a misconfiguration in Datadog. Investigating and fixing this can save unnecessary costs.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Implement Log Sampling&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;For repetitive or low-value logs, consider implementing sampling.&lt;/p&gt;
&lt;h3&gt;Trim Your Logs&lt;/h3&gt;
&lt;p&gt;Use abbreviations and cut down on spaces or symbols.&lt;/p&gt;
&lt;p&gt;e.g., mapping &lt;em&gt;AccountID&lt;/em&gt; to &lt;em&gt;aid&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Framework-generated logs often have a lot of bloat — perfect for optimization.&lt;/p&gt;
&lt;h3&gt;Avoid Polling and Preflight Requests&lt;/h3&gt;
&lt;p&gt;Eliminate unnecessary calls by understanding the flow from frontend to backend.&lt;/p&gt;
&lt;p&gt;Reducing transfer volume not only minimizes Datadog Forwarder execution costs but also significantly lowers cloud data transfer expenses.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Optimize APM&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Apply the same approach as log management.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Start with Visualization&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;See the sections above for actionable insights.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Exclude Low-Impact Proxy Services&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;For stable services with minimal insights, exclude them from APM monitoring to optimize data collection and save on costs.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Sample Detailed Trace&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Choose between sampling entire traces or targeting specific access points. With &lt;em&gt;DD_TRACE_SAMPLING_RULES&lt;/em&gt;, for instance, you can apply sampling specifically to database access traces.&lt;/p&gt;
&lt;h3&gt;Identify and Eliminate Duplicate Traces&lt;/h3&gt;
&lt;p&gt;Duplicate trace outputs can occur due to configuration errors in Datadog. Regularly audit and resolve these issues to avoid redundant costs.&lt;/p&gt;
&lt;h3&gt;Fix N+1 Query Issues&lt;/h3&gt;
&lt;p&gt;Avoiding N+1 queries is essential, as they can severely affect both performance and reliability while also driving up Datadog costs due to the increased number of spans.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Optimize Infrastructure Hosts&lt;/strong&gt;&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;Exclude Unmonitored Resources from AWS Integration&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Even EC2 hosts without the Datadog Agent are billed if they’re part of AWS Integration. If monitoring all EC2 instances isn’t necessary, exclude them from the AWS Integration settings. For setups with CloudWatch Metric Streams, additional steps are required. Remove EC2 from the namespace on AWS to stop data from being sent to Datadog.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Additional Usage Optimization&lt;/strong&gt;&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;Reduce API Test Frequency&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;API Test Billing is based on execution frequency, so running tests every minute costs 60 times more than hourly tests. In development environments, longer intervals are often sufficient. Adjust the frequency according to workload demands. Costs also scale with the number of locations. Limiting tests to a single region, such as Northern Virginia, can be an effective cost-saving measure. Dynamic interval adjustments based on time of day would be helpful, but this isn’t a built-in feature. You can implement such functionality using the Datadog API for custom configurations.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Optimize Sensitive Data Scanner&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Lambda logs, such as START, END, and REPORT logs, typically don’t contain sensitive data. Exclude these logs from scanning to reduce unnecessary cost.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/en/assets/datadog-cost-reduction-tips-c2e34f0bba8b-04.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Indexing and Sensitive Data Scanner require separate filter settings. If you want to apply exclusions collectively, you’ll need to exclude them from being sent on the client side.&lt;/p&gt;
&lt;h3&gt;Turn Off RUM Session Replay to Cut Costs If You Don’t Need It&lt;/h3&gt;
&lt;p&gt;RUM Premium, with its Session Replay feature, comes at a higher price than standard RUM.&lt;/p&gt;
&lt;p&gt;If Session Replay isn’t part of your workflow, double-check that &lt;em&gt;sessionReplaySampleRate&lt;/em&gt; is set to 0 to avoid unnecessary costs.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Datadog costs are closely tied to cloud costs. Look for solutions that can address both areas simultaneously for maximum efficiency.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This article is the English version of the original article written in Japanese, available&lt;/em&gt;&lt;a href=&quot;https://veritycost.com/posts/datadog-cost-reduction-tips-378b319c87d6/&quot;&gt;here&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>効率的な情報収集のポイント：見ないようにする</title><link>https://veritycost.com/posts/efficient-information-gathering-avoid-noise-3be7cf70544f/</link><guid isPermaLink="true">https://veritycost.com/posts/efficient-information-gathering-avoid-noise-3be7cf70544f/</guid><description>現代人は情報の量は十分でも情報の多さや品質に困っている人が多いようです。</description><pubDate>Sun, 23 Feb 2025 09:24:19 GMT</pubDate><content:encoded>&lt;p&gt;現代人は情報の量は十分でも情報の多さや品質に困っている人が多いようです。&lt;/p&gt;
&lt;p&gt;この状況を変えるには、良質なコンテンツを探すだけでは足りず、不要な情報をいかに避けるかがポイントです。&lt;/p&gt;
&lt;p&gt;このとき重要な概念がシグナル/ノイズ(S/N)比です。良質なコンテンツを増やすだけでは効果が薄く、ノイズを削ることで効率が一気に上昇します&lt;/p&gt;
&lt;p&gt;当記事では良質なコンテンツ(シグナル)の集め方ではなく、不要な情報(ノイズ)の避け方について記述します。前半は基本的な考え方を紹介し、後半は実際の手順を説明します。&lt;/p&gt;
&lt;p&gt;注意力は貴重な資産です。他者に奪われないように自分でコントロールしましょう。&lt;/p&gt;
&lt;h2&gt;トレンド/ランキングは無視&lt;/h2&gt;
&lt;p&gt;トレンドやランキングはまず役に立ちません。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;非プロフェッショナル層からの注目が集まっても価値が低い&lt;/li&gt;
&lt;li&gt;同じ層でもクオリティの低い話ほど注目が集まりがち&lt;/li&gt;
&lt;li&gt;みんな見るので差が生まれない&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;低い✕低いで時間の無駄です。専門誌サイトのランキングでも上位は低俗なものが上がることがあります。&lt;/p&gt;
&lt;p&gt;たまに役に立つという声もあると思いますが、むしろたまに役に立つことが問題です。脳の報酬系的には毎回報酬がもらえるよりランダムなほうが夢中になるとよく言われるところです。&lt;/p&gt;
&lt;p&gt;気合いで我慢するのは難しいので環境調整として非表示にしましょう。&lt;/p&gt;
&lt;h2&gt;おすすめ(レコメンド)にご用心&lt;/h2&gt;
&lt;p&gt;おすすめとはあなたが興味があるもの…という説明は嘘ではないのですが真実でもありません。実際のレコメンド対象は会社がより儲かるもの、短期的な刺激が得られるものです。刹那の娯楽にはなりますが長期的な目標達成の補助にはなりえません。うまく調整しましょう。調整できない場合は非表示にしたほうが良いです。&lt;/p&gt;
&lt;h3&gt;レコメンドの調整は難しい&lt;/h3&gt;
&lt;p&gt;レコメンドされたものに対して「興味なし」といった評価をしてもレコメンド傾向はほぼ変わりません。同じような興味ないものが表示されるだけでボタンを押す時間が無駄です。&lt;/p&gt;
&lt;p&gt;チャネルやユーザーごとミュートするか、いっそのこと要素非表示が最善です。&lt;/p&gt;
&lt;h2&gt;転載サイトは無視&lt;/h2&gt;
&lt;p&gt;Yahooニュースに代表される転載サイトを見ても、結局出典にあたる必要があるので時間の無駄です。&lt;/p&gt;
&lt;h2&gt;コメント欄は無視&lt;/h2&gt;
&lt;p&gt;これは特に日本のネット環境が特にそうなのですがコンテンツに対するコメントの質が低すぎます。&lt;/p&gt;
&lt;p&gt;そもそも目を通してすらいない人の感想を見ても無駄ですし、大喜利大会なら自分で主催したほうが楽しめます。そのコンテンツのマーケティングや広報をしていない限りは可能なら非表示にしたほうがよいです。&lt;/p&gt;
&lt;p&gt;住んでいるところの最新交通情報はともかく、専門分野で背景不明のどこの誰かすら不明な人の感想を検証するのは時間がかかるだけです。&lt;/p&gt;
&lt;h2&gt;惰性で同じものを見るのを辞め、卒業する&lt;/h2&gt;
&lt;p&gt;世の中にはもっと面白いものがあります。なんとなくではなく目的意識を持ち、より良いものを探しましょう。よく見てるサイトや人がどのような位置づけか意識できるようになってくれば、その分野への習熟度が上がってきた証です。&lt;/p&gt;
&lt;p&gt;最初は面白いことを言っていてもネタ切れで時事ネタばかりになるパターンもあります。卒業の季節です。&lt;/p&gt;
&lt;p&gt;エンタメの連載もの、連続ものには注意が必要です。作成側は次の回の気を引くべくテクニックを駆使してきます。相手は気を引くプロです。飽きてきたら卒業しましょう。&lt;/p&gt;
&lt;h2&gt;論点を明確化できたら放置する&lt;/h2&gt;
&lt;p&gt;気になる議論があったとします。論外のコメントを除外したとしても、外から眺めていると途中で議論が堂々巡りになっていることがほとんどです。&lt;/p&gt;
&lt;p&gt;いまはChatGPTもあり、賛成反対の論点の整理は簡単です。自分なりに整理できたあとは、決着しなかったり前に進まなかったりするようなら放置しましょう。&lt;/p&gt;
&lt;p&gt;議論に参加したくなるかもしれませんが、コピーした意見が増えるだけで新しい価値が足せないなら時間がもったいないですね。&lt;/p&gt;
&lt;h2&gt;ネット時事にコメントしない&lt;/h2&gt;
&lt;p&gt;そのコメントすると新しい価値が生まれますか？前に進みますか？&lt;/p&gt;
&lt;p&gt;いわゆる一丁噛みは時間がもったいないだけではなく、注意が意味不明な他人に奪われている証拠です。&lt;/p&gt;
&lt;p&gt;1年後にその事象が価値あるか考えてみましょう。&lt;/p&gt;
&lt;h2&gt;情報収集と娯楽を分ける&lt;/h2&gt;
&lt;p&gt;今やっていることは情報収集でしょうか？娯楽でしょうか？
情報収集のつもりがいつの間にか娯楽になってませんか？&lt;/p&gt;
&lt;p&gt;情報収集中に誘惑が多すぎて娯楽となりあっという間に時間が溶けることは避けたいですね。&lt;/p&gt;
&lt;p&gt;娯楽だと割り切ったとしても後で後悔するかどうか、明日もやりたいかどうかがポイントです。&lt;/p&gt;
&lt;p&gt;例えば音楽が好きで演奏が楽しく、もっとやりたいと思うならどんどんやりましょう。漫画を読むのもそうです。&lt;/p&gt;
&lt;p&gt;もし昨日を振り返ってこの時間が無駄だったと後悔するなら行動しましょう。もっと楽しいことは必ず見つかります。&lt;/p&gt;
&lt;h2&gt;無駄な情報を減らす環境を整備する方法(環境調整)&lt;/h2&gt;
&lt;p&gt;ここからは無駄な情報を減らす具体的な方法に入ります。&lt;/p&gt;
&lt;p&gt;サイトやサービス側は注意力を奪う達人であり、何十億何百億円とお金と人材をかけています。いくら努力して見ないようにしても正面から戦っては勝てません。&lt;/p&gt;
&lt;p&gt;注意を自分のものにするには事前の準備で決まります。我慢や努力ではなく、環境を整備することでネット時代に対応しましょう。&lt;/p&gt;
&lt;h2&gt;サイトの要素を非表示にする&lt;/h2&gt;
&lt;p&gt;見えなければ気になりません。&lt;/p&gt;
&lt;p&gt;PCならuBlock Origin Liteが便利です。広告ブロックが主な機能ですが、右クリックから要素ごと非表示(ブロック)にする機能が有益です。&lt;/p&gt;
&lt;h2&gt;サイトごとブロックする&lt;/h2&gt;
&lt;p&gt;見ることすら無駄なサイトもあります。例えばBlockSiteを使えばうっかりクリックしてもブロックされ大丈夫です。&lt;/p&gt;
&lt;p&gt;一つのツールだけにしたいならuBlock Origin Liteでページ丸ごと消すのも可能です。スマホの場合はやや難しいですがNextDNSあたりが無難です。&lt;/p&gt;
&lt;p&gt;Google検索から非表示にするにはuBlacklistが便利です。Yahooニュースを消すとすっきりします。noteなどのコンテンツサイトも工夫すれば特定のユーザーだけ非表示にできます。&lt;/p&gt;
&lt;h2&gt;通知を消す&lt;/h2&gt;
&lt;p&gt;標準設定では通知が多めな傾向にあります。必要最小限に絞りましょう。通知ゼロでOKなことも。サイト側の設定か、PCやスマホの設定で消せます。&lt;/p&gt;
&lt;h3&gt;メール通知も消す&lt;/h3&gt;
&lt;p&gt;何もしないと各サイトがメールでおすすめを送ってくることがあります。クオリティが低いようならメール設定画面から送信停止しましょう。&lt;/p&gt;
&lt;p&gt;送られてきたメールを毎回無視するのではなく元を断つ。地味な作業ですが効果的です。&lt;/p&gt;
&lt;h2&gt;Webサイトがあるアプリはアンインストールする&lt;/h2&gt;
&lt;p&gt;アプリのほうが便利なのは間違いないですが、その分注意力を奪われます。事業提供者側は摩擦(フリクション)を減らして使うハードルを下げ、たくさんの時間を使ってもらおうと一生懸命です。&lt;/p&gt;
&lt;p&gt;ここは逆にハードルを上げて不便にしましょう。&lt;/p&gt;
&lt;p&gt;また、前述の通りPCのほうがカスタマイズして不要なものを断てるため効果的です。&lt;/p&gt;
&lt;p&gt;ChatGPTなど作業ツール系のアプリならそのまま使ってもらってOKです。&lt;/p&gt;
&lt;h2&gt;事例: Twitter ( X )&lt;/h2&gt;
&lt;p&gt;まずはトレンドやおすすめ欄をひたすら除去します。&lt;/p&gt;
&lt;figure class=&quot;media-figure&quot;&gt;
  ![無駄を削ったあと](../../assets/efficient-information-gathering-avoid-noise-3be7cf70544f-01.png)
  &lt;figcaption&gt;無駄を削ったあと&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;きりがなくとも目に付くユーザーをひたすらミュートしていきましょう。ちょっと不快ならすぐミュートぐらいでちょうどよいです。表示する義務はありません。流行りもの系はハッシュタグ付きが多いのでキーワードミュートで消せます。&lt;/p&gt;
&lt;p&gt;フォローもどんどん外しましょう。フォローバックも義務ではありません。ネット有名人などをフォローするということは、結果的にその人の思想が自分の中に入っていくということです。その人の言動に似ていくということであり、本当にその人のようになりたいか判断しましょう。その人からしか取れない情報はありません。&lt;/p&gt;
&lt;p&gt;ポストは面白くてもリポストがいまいちなタイプの人もいます。対象者のリポストをオフにする機能を使いましょう。&lt;/p&gt;
&lt;figure class=&quot;media-figure&quot;&gt;
  ![bskyにも欲しい](../../assets/efficient-information-gathering-avoid-noise-3be7cf70544f-02.png)
  &lt;figcaption&gt;bskyにも欲しい&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;ユーザーが多いだけあり、上位互換の優れた人は探せばいます。そのような人はフォロワーが少ない傾向にあります。&lt;/p&gt;
&lt;p&gt;情報をただ転送しているだけな人の話はそのジャンルの入門フェーズ以外は避け、時が来たら卒業しましょう。(その後は実務家の実践だけを見ていくとよいです。)&lt;/p&gt;
&lt;p&gt;タイムラインについて、「おすすめ」欄をしばらく眺めるのは良いのですが、見ている間にフォローしていない人が表示されたら切り替えの合図です。これ以上は見るだけ無駄というサインなので、「フォロー中」欄に切り替えるか他のことをしましょう。&lt;/p&gt;
&lt;p&gt;標準設定ではメールをやたら送ってくるので設定を変更しましょう。&lt;/p&gt;
&lt;h3&gt;そもそも見る価値はあるのか&lt;/h3&gt;
&lt;p&gt;英語圏の多くの専門分野の専門家はBlueSkyに脱出してしまったので(e.g. IT, 学術)、あまり見る価値はありません。&lt;/p&gt;
&lt;p&gt;日本を対象とした分野の専門家(e.g. 国内旅行)はまだXに多いので興味あるジャンル次第です。&lt;/p&gt;
&lt;p&gt;全体的には衰退傾向にあり復活はないでしょう。&lt;/p&gt;
&lt;h3&gt;目的を決める&lt;/h3&gt;
&lt;p&gt;情報収集なのか、売り込みにつなげたいのか、不特定多数から注目を浴びたいのか。目的をはっきりさせ、目的に繋がらないことはやらないことでSNSを乗りこなすことができます。できなければSNSに乗りこなされます。&lt;/p&gt;
&lt;h2&gt;事例: YouTube&lt;/h2&gt;
&lt;p&gt;チャネルごと非表示にできるのは良い点です。最初はレコメンドがひどいので積極的に非表示にしましょう。ある程度こなすと安定してきます。&lt;/p&gt;
&lt;figure class=&quot;media-figure&quot;&gt;
  ![興味なしは効果なし。チャネルごと消しましょう](../../assets/efficient-information-gathering-avoid-noise-3be7cf70544f-03.png)
  &lt;figcaption&gt;興味なしは効果なし。チャネルごと消しましょう&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;コメント欄は非表示に。コメントが役に立ったことはありません。&lt;/p&gt;
&lt;figure class=&quot;media-figure&quot;&gt;
  ![すっきり！YouTubeのコメントはコメント者の背景がわからないので地獄](../../assets/efficient-information-gathering-avoid-noise-3be7cf70544f.png)
  &lt;figcaption&gt;すっきり！YouTubeのコメントはコメント者の背景がわからないので地獄&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;トップ画面のサイドバーも非表示にすると余計なトレンドなども消せます。&lt;/p&gt;
&lt;figure class=&quot;media-figure&quot;&gt;
  ![平和なサイドバー](../../assets/efficient-information-gathering-avoid-noise-3be7cf70544f-05.png)
  &lt;figcaption&gt;平和なサイドバー&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;さいごに&lt;/h2&gt;
&lt;p&gt;今やってることが生きがいなら問題ないのですが、時間を無駄にしたと後悔することがあるなら事前準備しましょう。&lt;/p&gt;
&lt;p&gt;たかがネットに見えても、どれくらい1日に時間を使っているか測れば凄まじい量の時間を消化していることが見える時代です。&lt;/p&gt;
&lt;p&gt;無駄を削ってもっと楽しいことをしましょう。&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>外部プラットフォームと内部プラットフォーム: そのリスクと方針</title><link>https://veritycost.com/posts/external-internal-platform-risks-policy-6421479380c6/</link><guid isPermaLink="true">https://veritycost.com/posts/external-internal-platform-risks-policy-6421479380c6/</guid><description>ある程度組織が大きくなってくると、プラットフォームの必要性が叫ばれます。一方で、プラットフォームに関わったことがある人は少ないようです。</description><pubDate>Tue, 09 Jan 2024 05:50:12 GMT</pubDate><content:encoded>&lt;p&gt;ある程度組織が大きくなってくると、プラットフォームの必要性が叫ばれます。一方で、プラットフォームに関わったことがある人は少ないようです。&lt;/p&gt;
&lt;p&gt;プラットフォームエンジニアリングは本当に必要なのでしょうか？&lt;/p&gt;
&lt;p&gt;この記事ではプラットフォームで混同されがちな概念を説明し、(内部)プラットフォーム作成のヒントを解説します。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;外部プラットフォームとは&lt;/li&gt;
&lt;li&gt;内部プラットフォームとは&lt;/li&gt;
&lt;li&gt;内部プラットフォームの典型例&lt;/li&gt;
&lt;li&gt;プラットフォームのリスク&lt;/li&gt;
&lt;li&gt;必要なのか？&lt;/li&gt;
&lt;li&gt;立ち上げ方針&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;まずは混同されがちな外部プラットフォームと内部プラットフォームの違いからです。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/external-internal-platform-risks-policy-6421479380c6-01.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;外部プラットフォーム a.k.a プラットフォームビジネス&lt;/h2&gt;
&lt;p&gt;外部プラットフォームとはビジネス的なプラットフォームを指します。&lt;/p&gt;
&lt;p&gt;Amazon.co.jpで探せるプラットフォームが題名にある書籍のほとんど、プラットフォームビジネスで出てくるほうです。エンジニア以外がプラットフォームと発話したらこれです。プラットフォームを提供する主体はプラットフォーマーと呼ばれます。よく外部にAPIを提供していますね。&lt;/p&gt;
&lt;p&gt;プラットフォームを作ったときには想像もしていなかったようなことを、プラットフォームユーザーが生み出すことができます。新たなビジネスが生まれ、エコシステムができるかもしれません。&lt;/p&gt;
&lt;p&gt;Amazon社のプラットフォームが有名です。フライホイールの図は見たことある人も多いでしょう。&lt;/p&gt;
&lt;p&gt;プロフィットセンターであり、組織としては限界まで投資可能です。&lt;/p&gt;
&lt;p&gt;ユーザーは組織外がほとんどです。顧客や協業先が代表的。&lt;/p&gt;
&lt;p&gt;積極的に使ってもらうことに価値があり、そのためDevExやDevRelが効果的です。&lt;/p&gt;
&lt;p&gt;ここのゲームプレイに関しては別稿に譲ります。&lt;/p&gt;
&lt;h2&gt;内部プラットフォーム≒プラットフォームエンジニアリング&lt;/h2&gt;
&lt;p&gt;いわゆる共通基盤です。組織内部のサービスを共通化することで無駄を省きます。内部のQCD改善が目標です。&lt;/p&gt;
&lt;p&gt;コストセンターであり、どこまで投資するか難しい判断が求められます。(無駄をなくす目的なのにあまりにも多くの人員を割いたら本末転倒です。)&lt;/p&gt;
&lt;p&gt;ユーザーはほぼ内部顧客です。組織内の別のサービスであることが多いでしょう。&lt;/p&gt;
&lt;p&gt;Team Topologyのplatform team が扱うものです。DevExは欲しいところですが、現実的にはそこまで工数をかけられないでしょう。内部限定でありハイコンテキストを前提とすれば、完全なドキュメント等がなくても組織内でカバーできます。&lt;/p&gt;
&lt;h2&gt;内部プラットフォームの典型例&lt;/h2&gt;
&lt;p&gt;認証認可基盤 ユーザー基盤 決済基盤 ログ基盤 測定基盤などが代表的です。&lt;/p&gt;
&lt;p&gt;開発生産性(development productivity)向上系のものもあります。CI/CD、IaCなど。&lt;/p&gt;
&lt;h2&gt;内部プラットフォームのリスク&lt;/h2&gt;
&lt;p&gt;「同じものが2つ以上あると非効率だから共通化しよう！」「今後必要になりそうだから共通化できそうなところを切り出しておこう！」&lt;/p&gt;
&lt;p&gt;その通りなのですが、なかなかそううまくはいかない現実があります。&lt;/p&gt;
&lt;h3&gt;単一障害点になる&lt;/h3&gt;
&lt;p&gt;プロダクトAとプロダクトBが同期リクエスト形式で依存するプロダクトPがあるとして、プロダクトPが障害になったら全部障害です。&lt;/p&gt;
&lt;p&gt;プロダクトAに高信頼性が求められるなら、プロダクトPはそれを遥かに上回る信頼性が必要です。&lt;/p&gt;
&lt;h3&gt;複雑化する&lt;/h3&gt;
&lt;p&gt;共通部分を切り出したら見落としがよくなるように見えます。
しかし、長く時間経つとプラットフォーム側が複雑になり、見通しが悪化することがよくあります。特にビジネス優先（クライアント優先）だとプラットフォーム側にロジックを寄せることがありがちです。&lt;/p&gt;
&lt;figure class=&quot;media-figure&quot;&gt;
  &lt;blockquote class=&quot;twitter-tweet&quot; data-media-max-width=&quot;560&quot;&gt;
    &lt;p lang=&quot;ja&quot; dir=&quot;ltr&quot;&gt;
      クソコード動画「共通化の罠」 &lt;a href=&quot;https://t.co/MM750CNXc2&quot;&gt;pic.twitter.com/MM750CNXc2&lt;/a&gt;
    &lt;/p&gt;
    — ミノ駆動 (@MinoDriven){&quot; &quot;}
    &lt;a href=&quot;https://twitter.com/MinoDriven/status/1127539251761909760?ref_src=twsrc%5Etfw&quot;&gt;
      May 12, 2019
    &lt;/a&gt;
  &lt;/blockquote&gt;{&quot; &quot;}
  &lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
  &lt;figcaption&gt;わかりやすい動画。サービスでも同じことが起きます&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3&gt;改修速度が落ちる&lt;/h3&gt;
&lt;p&gt;その領域に特化したチームを用意したのだから開発速度が上がりそうですが、悪化することはよくあります。&lt;/p&gt;
&lt;p&gt;何につけても多数の影響範囲を確認しながら作業する必要があるからです。影響範囲が存在するならクライアント側それぞれの事情を加味して調整が必要です。特にクライアント側の対応が発生するときが一大プロジェクトになります。&lt;/p&gt;
&lt;p&gt;純粋に開発速度で負けることもあります。ゴールデンイメージとしてAWSなどをラップしたものを作ったものの、その改善速度についていけないといったことです。大手プロバイダーとはその部分にアサインできる人数に差がありますね。IaCの社内共通モジュールが化石化しがちです。&lt;/p&gt;
&lt;p&gt;外部の基盤に不足している機能を補うべくOSSや社内ツールが生まれ、外部基盤側が新バージョンでその機能を追加したので不要になるが、そのツールに依存してしまい乗り換えできず、新バージョンが使えなくなり非効率となるというサイクルを歴史的に見てここ数十年繰り返していますね。なにもしなかったらいつも最新バージョンが使えるので悲しいことです。&lt;/p&gt;
&lt;p&gt;そしてソフトウエア開発において速度の悪化はコストや品質の悪化を招く要因の一つです。&lt;/p&gt;
&lt;h3&gt;優先順位がつけられない。不満がたまる&lt;/h3&gt;
&lt;p&gt;さまざまなクライアントからの改修要望についても優先順位をつけるのも大変で、優先順位を調整する作業だけでも工数が多くかかります。&lt;/p&gt;
&lt;p&gt;優先順位をつけるメカニズム例として、話し合い方式、トークン方式、HiPPO方式、先着順、声が大きい人優先などがあります。&lt;/p&gt;
&lt;p&gt;どうしてもクライアントのチームとは優先順位が異なるためプラットフォームに対する不満は増える一方です。&lt;/p&gt;
&lt;p&gt;改修スケジュールが見えないので、結局クライアント側でその機能を作り、それが二重三重にもなることも。&lt;/p&gt;
&lt;p&gt;(その機能要望ですか？最速で3年後に着手開始です)&lt;/p&gt;
&lt;h3&gt;共有地の悲劇&lt;/h3&gt;
&lt;p&gt;n+1や重いクエリのような非効率なプラットフォームを呼び出す処理があったとしても、各クライアントには改修する動機があまりありません。自分のチーム内なら確認するでしょうが、他チームはそもそも影響が見えないし見方もわからないでしょう。&lt;/p&gt;
&lt;p&gt;そしてプラットフォームが障害になり、そのまま全プロダクトが大障害になることも。&lt;/p&gt;
&lt;h3&gt;プラットフォーム内はお互いに無関係&lt;/h3&gt;
&lt;p&gt;プラットフォーム内の各サービスについて、「共通」という部分は共通点がありますが、扱っているドメインはバラバラです。
会社組織をバックオフィスでくくったとして法務と税務、HRやファイナンスはやることが違いすぎるでしょう。分類するときに「その他」になんでもかんでも放り投げているイメージです。&lt;/p&gt;
&lt;p&gt;特にプラットフォーム部門で組織目標や方針を立てるときに影響します。&lt;/p&gt;
&lt;h3&gt;需要がない&lt;/h3&gt;
&lt;p&gt;新プロジェクトで必要な機能について、共通基盤側で作ると効率的に見えます。今後他のチームでも使うかもしれないですよね。&lt;/p&gt;
&lt;p&gt;しかし、新プロジェクトが消滅したり、優先順位が変わることでその基盤部分が不要になることは時折あります。手を付けていれば残念ながら貴重な工数と特に時間を無駄にしたことになります。仮に1年延期だとしても一手無駄にしていますね。特にスタートアップだと厳しい状況に。&lt;/p&gt;
&lt;p&gt;需要があるという仮説が検証されないまま作ってしまったプロダクトです。使う予定や使いたいという言葉と、実際に使っている状態では天と地ほどの差があります。&lt;/p&gt;
&lt;h3&gt;QCDが悪化&lt;/h3&gt;
&lt;p&gt;QCDを改善するはずが、長年経つとむしろQuality Cost Deliveryが悪化しているように外から見えます。遅い高いおいしくない状態です。&lt;/p&gt;
&lt;h2&gt;内部プラットフォームは必要なのか？&lt;/h2&gt;
&lt;p&gt;プラットフォームの存在意義で致命的なところは共通部分ということです。組織内部のサービスで共通ということは、組織の外部でも共通であり、トレンドを見渡せばコモディティ化が進みます。陳腐化済みのものを開発するメリットはありません。&lt;/p&gt;
&lt;p&gt;特化した外部SaaSなら同じ領域でもプロフィットセンターとなり、プロダクトマネジメント、デザイン、DevExに投資できます。プロダクト品質にも差が出ます。&lt;/p&gt;
&lt;p&gt;エンジニアの立場からすると、内部プラットフォームはすでに世の中にあるものを作るだけなので面白みに欠けることがあります。例えば決済に関心があれば決済のSaaSにいったほうが面白い問題に取り組めます。もしくは自分たちで新しくプロダクトを立ち上げるか。(そういうスタートアップもよく見かけます)&lt;/p&gt;
&lt;p&gt;ただ、現在、プラットフォームを代替できるようなSaaSはコスト面で不利です。ユーザー数や利用量課金のため、最終的に自分たちで作成したほうが人件費を加味しても安くなる傾向にあります。&lt;/p&gt;
&lt;p&gt;また、同様の機能が多数存在すると、セキュリティ、コンプライアンス面で効率が悪くリスクが高くなることは否めません。デザイン/UX面でも強い一貫性を保ちたいところです。&lt;/p&gt;
&lt;p&gt;組織の規模がハイパースケーラーレベルなら外部SaaSより難しい問題にエンジニアが取り組めるかもしれません。残念ながら日本企業でそのレベルに到達できた企業は厳し目に見て存在しません。&lt;/p&gt;
&lt;p&gt;もちろん適切に作成できた基盤は使い回しが容易でプロダクト立ち上げがスムーズになります。Meta社のThreadsが約5ヶ月でローンチしながら、高いスケーラビリティやセキュリティ、品質を担保できたことはプラットフォームがあるからこそです。&lt;/p&gt;
&lt;h2&gt;プラットフォームの立ち上げ方針&lt;/h2&gt;
&lt;h3&gt;既存の主プロダクトからサービスを抽出し分離する&lt;/h3&gt;
&lt;p&gt;主プロダクトから抽出すれば、その部分は需要があることが既に証明されています。主プロダクトは肥大化しがちなので分離できるとお互いにメリットがあります。&lt;/p&gt;
&lt;h3&gt;人を集める&lt;/h3&gt;
&lt;p&gt;エンジニアリング難易度は高く、組織内で最高のアクセス量や高信頼性が求められます。そのような分野に関心がある方を募集しましょう。プラットフォームは関係者が多く調整も難しいですが、それを逆手に取って難易度が高いことを歓迎する人材がよいですね。&lt;/p&gt;
&lt;h3&gt;なるべくプロダクトとして扱う&lt;/h3&gt;
&lt;p&gt;せっかく作っても使われなかったら意味がありません。乗り換えのメリットを打ち出していきましょう。&lt;/p&gt;
&lt;p&gt;通常のプロダクト同様、プロダクトマネジメントとして内部のドキュメント、デリバリ、マーケティング、ヒアリング、UX(DevEx)改善をしていきましょう。測定も忘れずに。工数は限られていますがここにも投資しましょう。&lt;/p&gt;
&lt;h3&gt;コピーアンドペーストの勝利(?)&lt;/h3&gt;
&lt;p&gt;内部でレジストリを作成してバージョン管理してコンポーネントを再利用できるようにする…というのは理想ですが、現実はクライアントはバージョンアップしませんしバージョン上げるとおかしくなるでしょう。(言語やフレームワークを常に最新化できている組織なら心配ありません。)&lt;/p&gt;
&lt;p&gt;ライブラリやIaC周りはサンプルだけ提供して真似してもらうほうがよいです。個別に進化してもらい、フィードバックをもらい反映しましょう。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://ui.shadcn.com/docs&quot;&gt;shadcn&lt;/a&gt;のようなイメージ。&lt;/p&gt;
&lt;p&gt;本当に内部の需要が多いことが証明されたらコンポーネント化し、サービス化を狙っていくのもよいですね。&lt;/p&gt;
&lt;h3&gt;障害時の動作を検討する&lt;/h3&gt;
&lt;p&gt;中央集権型だと障害時に全滅するため、レプリケーションによる分散という方法もあります。一方、データ同期やstale cacheといった難しい問題が生まれます。&lt;/p&gt;
&lt;p&gt;なるべくイベント型で非同期にしたほうが障害に強いものの、データ復旧といった部分には違った難しさがあります。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Everything fails&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;ということで分散システムでは必ず部分が壊れるため対応を検討しましょう。&lt;/p&gt;
&lt;h3&gt;リソース不足で要求に答えれないときは要求元からのレンタルを検討する&lt;/h3&gt;
&lt;p&gt;要求元が人員を貸し出し、要求先の一員として同じ基盤上に同じ開発スタイル、開発スタックで作成するとスムーズに進みます。
(Away Team)&lt;/p&gt;
&lt;h2&gt;Limitation&lt;/h2&gt;
&lt;p&gt;この記事ではPlatform Engineering自体は説明していません。チーム組成やKPIの難しさはプロダクトとはまた別にあります。&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;組織によってはエンジニアの数十パーセントがプラットフォームが占めることがあります。プラットフォーム自体が負債になりえるのでうまく計画しましょう。&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>マジックナンバー化による高速な曜日計算</title><link>https://veritycost.com/posts/fast-day-of-week-calc-81ee505e/</link><guid isPermaLink="true">https://veritycost.com/posts/fast-day-of-week-calc-81ee505e/</guid><description>sakamoto algorithm以上に高速化を図った曜日計算の紹介</description><pubDate>Mon, 24 Aug 2026 15:00:00 GMT</pubDate><content:encoded>&lt;p&gt;曜日を求めるアルゴリズムとして、Tomohiko Sakamotoの手法がよく知られています。&lt;/p&gt;
&lt;p&gt;この記事では、対象年を1年から9999年に限定し、Sakamotoの計算に含まれる定数除算と&lt;code&gt;mod 7&lt;/code&gt;を、乗算・シフト・ビット抽出に置き換えます。&lt;/p&gt;
&lt;p&gt;Apple Silicon上でCのインライン・バッチ処理を測定したところ、通常のSakamoto実装に対して約30%の高速化を確認しました。&lt;/p&gt;
&lt;p&gt;最初に完成した実装を紹介し、その後で定数&lt;code&gt;5243&lt;/code&gt;と&lt;code&gt;74899&lt;/code&gt;が機能する理由を説明します。&lt;/p&gt;
&lt;h2&gt;実装例&lt;/h2&gt;
&lt;h3&gt;TypeScript&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;/** Returns 0=Sunday, ..., 6=Saturday.
 *
 * Preconditions:
 * - year: 1..9999
 * - month: 1..12
 * - day: 1..31
 */
const SCALED_MONTH_TERM = new Uint32Array([
  0, 224_697, 149_798, 374_495, 0, 224_697, 374_495, 74_899, 299_596, 449_394, 149_798, 299_596,
]);

export function weekdayBoundedDistributed(year: number, month: number, day: number): number {
  year -= Number(month &amp;#x3C; 3);

  const product = Math.imul(year, 5_243);
  const base = year + (year &gt;&gt;&gt; 2) - (product &gt;&gt;&gt; 19) + (product &gt;&gt;&gt; 21) + day;

  return ((Math.imul(base, 74_899) + SCALED_MONTH_TERM[month - 1]) &gt;&gt;&gt; 16) &amp;#x26; 7;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;TypeScriptでは、32-bit整数乗算を明示するために&lt;code&gt;Math.imul&lt;/code&gt;を使用しています。また、右シフトには符号なし右シフトの&lt;code&gt;&gt;&gt;&gt;&lt;/code&gt;を使用しています。&lt;/p&gt;
&lt;h3&gt;Rust&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;/// Returns 0=Sunday, ..., 6=Saturday.
///
/// Preconditions:
/// - year: 1..9999
/// - month: 1..12
/// - day: 1..31
#[inline]
pub fn weekday_bounded_distributed(
    mut year: u32,
    month: u32,
    day: u32,
) -&gt; u32 {
    const SCALED_MONTH_TERM: [u32; 12] = [
        0, 224_697, 149_798, 374_495,
        0, 224_697, 374_495, 74_899,
        299_596, 449_394, 149_798, 299_596,
    ];

    year -= (month &amp;#x3C; 3) as u32;

    let product = year * 5_243;

    let base = year
        + (year &gt;&gt; 2)
        - (product &gt;&gt; 19)
        + (product &gt;&gt; 21)
        + day;

    (
        (
            base * 74_899 +
            SCALED_MONTH_TERM[(month - 1) as usize]
        ) &gt;&gt; 16
    ) &amp;#x26; 7
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;C&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;#include &amp;#x3C;stdint.h&gt;

/*
 * Returns 0=Sunday, ..., 6=Saturday.
 *
 * Preconditions:
 * - year: 1..9999
 * - month: 1..12
 * - day: 1..31
 */
static inline uint32_t weekday_bounded_distributed(
    uint32_t year,
    uint32_t month,
    uint32_t day
) {
    static const uint32_t scaled_month_term[12] = {
        0u, 224697u, 149798u, 374495u,
        0u, 224697u, 374495u, 74899u,
        299596u, 449394u, 149798u, 299596u
    };

    year -= month &amp;#x3C; 3;

    const uint32_t product = year * 5243u;

    const uint32_t base = year
        + (year &gt;&gt; 2)
        - (product &gt;&gt; 19)
        + (product &gt;&gt; 21)
        + day;

    return (
        (
            base * 74899u +
            scaled_month_term[month - 1]
        ) &gt;&gt; 16
    ) &amp;#x26; 7u;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;通常のSakamotoの手法&lt;/h2&gt;
&lt;p&gt;Sakamotoの手法では、次の月補正値を使用します。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cで書くと、基本形は次のようになります。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;static uint32_t weekday_sakamoto(
    uint32_t year,
    uint32_t month,
    uint32_t day
) {
    static const uint8_t month_term[12] = {
        0, 3, 2, 5, 0, 3,
        5, 1, 4, 6, 2, 4
    };

    year -= month &amp;#x3C; 3;

    return (
        year
        + year / 4
        - year / 100
        + year / 400
        + month_term[month - 1]
        + day
    ) % 7;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;1月と2月を前年の13月、14月に相当するものとして扱うため、&lt;code&gt;month &amp;#x3C; 3&lt;/code&gt;の場合に年を1減らします。&lt;/p&gt;
&lt;p&gt;その後、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;year
+ floor(year / 4)
- floor(year / 100)
+ floor(year / 400)
+ month term
+ day
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;を計算し、7で割った余りを曜日とします。&lt;/p&gt;
&lt;p&gt;Sakamotoの手法については、次の記事でも実装と考え方が紹介されています。&lt;/p&gt;
&lt;h2&gt;マジックナンバー化&lt;/h2&gt;
&lt;p&gt;今回の検討は、Falk Hüffner氏による次の記事をきっかけに始めました。&lt;/p&gt;
&lt;p&gt;この記事では、入力範囲を限定することで、うるう年判定を乗算、マスク、比較による短い式へ変換しています。&lt;/p&gt;
&lt;p&gt;同じように、曜日計算でも入力範囲を限定すれば、Sakamoto式の定数除算や剰余演算を短い整数演算へ変換できるのではないかと考えました。&lt;/p&gt;
&lt;p&gt;今回の実装では、年を1以上9999以下に限定しています。&lt;/p&gt;
&lt;p&gt;1月と2月について年を1減らした後でも、年の値は次の範囲に収まります。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 &amp;#x3C;= year &amp;#x3C;= 9999
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;この限定範囲を利用して、次の二つの変換を行います。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;const uint32_t product = year * 5243u;

product &gt;&gt; 19; // year / 100
product &gt;&gt; 21; // year / 400
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;さらに、Sakamoto式の中間値を&lt;code&gt;value&lt;/code&gt;とすると、次の式で&lt;code&gt;value % 7&lt;/code&gt;を求められます。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;((value * 74899u) &gt;&gt; 16) &amp;#x26; 7u
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;今回のポイントは、次の二つの定数です。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5243
74899
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ただし、&lt;code&gt;5243&lt;/code&gt;を使った0以上9999以下の整数の100除算は既知の最適化(strength reduction)です。&lt;/p&gt;
&lt;p&gt;今回の実装では、同じ積&lt;code&gt;year * 5243&lt;/code&gt;から&lt;code&gt;year / 100&lt;/code&gt;と&lt;code&gt;year / 400&lt;/code&gt;の両方を取り出しています。こちらについては後ほど説明します。&lt;/p&gt;
&lt;h2&gt;月補正値の分配&lt;/h2&gt;
&lt;p&gt;最初に作成した実装では、通常の月補正値を加えてから、全体を&lt;code&gt;74899&lt;/code&gt;倍していました。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;value =
    year_term
    + month_term[month - 1]
    + day;

weekday =
    ((value * 74899u) &gt;&gt; 16) &amp;#x26; 7u;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これでも通常のSakamoto式より高速でしたが、月補正値の加算が最終乗算の前にあるため、計算の依存鎖に含まれます。&lt;/p&gt;
&lt;p&gt;そこで、乗算の分配法則を使います。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(year term + month term + day) * 74899

= (year term + day) * 74899
  + month term * 74899
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;月補正値は12個しかないため、あらかじめ&lt;code&gt;74899&lt;/code&gt;倍した値を保持できます。&lt;/p&gt;
&lt;p&gt;通常の月補正値は次のとおりです。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これを&lt;code&gt;74899&lt;/code&gt;倍すると、次の値になります。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0, 224697, 149798, 374495,
0, 224697, 374495, 74899,
299596, 449394, 149798, 299596
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;この変形により、月補正値の加算を最終乗算の後へ移動できます。&lt;/p&gt;
&lt;p&gt;Apple Silicon上のClangでは、この部分が積和命令を使ったコードへコンパイルされました。&lt;/p&gt;
&lt;p&gt;月テーブルは12バイトから48バイトへ増えますが、年ごとのテーブルや曜日結果のテーブルを追加するわけではありません。一般的なPCやサーバーではキャッシュライン1本に収まる大きさです。メモリやコードサイズを優先する環境では、12バイトの通常月テーブルを使う分配前の実装も選択できます。&lt;/p&gt;
&lt;h2&gt;なぜ&lt;code&gt;5243&lt;/code&gt;で100除算できるのか&lt;/h2&gt;
&lt;p&gt;補正後の年を&lt;code&gt;year&lt;/code&gt;とします。&lt;/p&gt;
&lt;p&gt;対象範囲は次のとおりです。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 &amp;#x3C;= year &amp;#x3C;= 9999
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;year&lt;/code&gt;を次のように分解します。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;year = 100 * q + r
0 &amp;#x3C;= r &amp;#x3C; 100
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ここで、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5243 * 100
= 524300
= 2^19 + 12
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;です。&lt;/p&gt;
&lt;p&gt;したがって、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5243 * year

= 5243 * (100 * q + r)

= 2^19 * q
  + 12 * q
  + 5243 * r
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;となります。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;year &amp;#x3C;= 9999&lt;/code&gt;なので、&lt;code&gt;q &amp;#x3C;= 99&lt;/code&gt;かつ&lt;code&gt;r &amp;#x3C;= 99&lt;/code&gt;です。&lt;/p&gt;
&lt;p&gt;残りの部分の最大値は、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;12 * 99 + 5243 * 99
= 520245
&amp;#x3C; 2^19
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;です。&lt;/p&gt;
&lt;p&gt;つまり、&lt;code&gt;12 * q + 5243 * r&lt;/code&gt;から19ビット目への桁上がりはありません。&lt;/p&gt;
&lt;p&gt;そのため、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;(year * 5243u) &gt;&gt; 19
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;は、対象範囲で常に&lt;code&gt;year / 100&lt;/code&gt;と一致します。&lt;/p&gt;
&lt;h2&gt;同じ積で400除算できる理由&lt;/h2&gt;
&lt;p&gt;今度は&lt;code&gt;year&lt;/code&gt;を次のように分解します。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;year = 400 * q + r
0 &amp;#x3C;= r &amp;#x3C; 400
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;また、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5243 * 400
= 2097200
= 2^21 + 48
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;です。&lt;/p&gt;
&lt;p&gt;したがって、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5243 * year

= 2^21 * q
  + 48 * q
  + 5243 * r
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;となります。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;year &amp;#x3C;= 9999&lt;/code&gt;より、&lt;code&gt;q &amp;#x3C;= 24&lt;/code&gt;かつ&lt;code&gt;r &amp;#x3C;= 399&lt;/code&gt;です。&lt;/p&gt;
&lt;p&gt;残りの部分の最大値は、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;48 * 24 + 5243 * 399
= 2093109
&amp;#x3C; 2^21
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;です。&lt;/p&gt;
&lt;p&gt;したがって、21ビット目への桁上がりはありません。&lt;/p&gt;
&lt;p&gt;そのため、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;(year * 5243u) &gt;&gt; 21
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;は、対象範囲で&lt;code&gt;year / 400&lt;/code&gt;と一致します。&lt;/p&gt;
&lt;p&gt;以上から、一度の乗算結果を共有して次の二つの値を取得できます。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;const uint32_t product = year * 5243u;

const uint32_t century = product &gt;&gt; 19;
const uint32_t four_centuries = product &gt;&gt; 21;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;なぜ&lt;code&gt;74899&lt;/code&gt;で&lt;code&gt;mod 7&lt;/code&gt;を計算できるのか&lt;/h2&gt;
&lt;p&gt;Sakamoto式の途中結果を&lt;code&gt;value&lt;/code&gt;とします。&lt;/p&gt;
&lt;p&gt;今回の入力範囲では、&lt;code&gt;value&lt;/code&gt;は次の範囲に収まります。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 &amp;#x3C;= value &amp;#x3C; 13000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;value&lt;/code&gt;を次のように分解します。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;value = 7 * q + r
0 &amp;#x3C;= r &amp;#x3C; 7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;定数&lt;code&gt;74899&lt;/code&gt;には、次の関係があります。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;7 * 74899
= 8 * 2^16 + 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;また、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;74899
= 2^16 + 9363
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;です。&lt;/p&gt;
&lt;p&gt;これらを使って展開すると、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;74899 * value

= 74899 * (7 * q + r)

= (8 * q + r) * 2^16
  + 5 * q
  + 9363 * r
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;となります。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;value &amp;#x3C; 13000&lt;/code&gt;なので、&lt;code&gt;q &amp;#x3C;= 1857&lt;/code&gt;です。&lt;/p&gt;
&lt;p&gt;残りの部分の最大値は、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5 * 1857 + 9363 * 6
= 65463
&amp;#x3C; 2^16
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;です。&lt;/p&gt;
&lt;p&gt;したがって、&lt;code&gt;5 * q + 9363 * r&lt;/code&gt;から16ビット目への桁上がりはありません。&lt;/p&gt;
&lt;p&gt;そのため、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(74899 * value) &gt;&gt; 16
= 8 * q + r
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;となります。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;8 * q&lt;/code&gt;の下位3ビットは必ずゼロです。&lt;/p&gt;
&lt;p&gt;したがって、下位3ビットを取り出せば&lt;code&gt;r&lt;/code&gt;だけが残ります。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;((value * 74899u) &gt;&gt; 16) &amp;#x26; 7u
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これは対象範囲で&lt;code&gt;value % 7&lt;/code&gt;と一致します。&lt;/p&gt;
&lt;h2&gt;32-bit整数の範囲&lt;/h2&gt;
&lt;p&gt;この実装は、32-bit整数のオーバーフローを利用していません。&lt;/p&gt;
&lt;p&gt;最初の積の最大値は次のとおりです。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;9999 * 5243
= 52424757
&amp;#x3C; 2^32
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最終段について、&lt;code&gt;value &amp;#x3C; 13000&lt;/code&gt;という少し広い上限を使っても、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;12999 * 74899
= 973612101
&amp;#x3C; 2^32
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;です。&lt;/p&gt;
&lt;p&gt;したがって、すべての積は32-bit符号なし整数に収まります。&lt;/p&gt;
&lt;h2&gt;全入力での検証&lt;/h2&gt;
&lt;p&gt;今回のアルゴリズムは年1〜9999に限定しています。&lt;/p&gt;
&lt;p&gt;そのため、対象となるすべての入力について、通常のSakamoto式と比較できます。&lt;/p&gt;
&lt;p&gt;次はTypeScriptによる全件検証です。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;const monthTerm = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4] as const;

function weekdaySakamoto(year: number, month: number, day: number): number {
  year -= Number(month &amp;#x3C; 3);

  return (
    (year +
      Math.floor(year / 4) -
      Math.floor(year / 100) +
      Math.floor(year / 400) +
      monthTerm[month - 1] +
      day) %
    7
  );
}

for (let year = 1; year &amp;#x3C;= 9_999; year++) {
  for (let month = 1; month &amp;#x3C;= 12; month++) {
    for (let day = 1; day &amp;#x3C;= 31; day++) {
      const expected = weekdaySakamoto(year, month, day);

      const actual = weekdayBoundedDistributed(year, month, day);

      if (actual !== expected) {
        throw new Error(`mismatch: ${year}-${month}-${day}: ` + `${actual} !== ${expected}`);
      }
    }
  }
}

console.log(&quot;verified&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;比較する入力数は次のとおりです。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;9999 * 12 * 31
= 3,719,628
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;年1〜9999、月1〜12、日1〜31のすべてで、通常のSakamoto式との一致を確認しました。&lt;/p&gt;
&lt;p&gt;日については各月の日数を考慮せず、すべて31日まで検証しています。したがって、実在する日付より広い入力集合で一致しています。&lt;/p&gt;
&lt;p&gt;この関数自体は、4月31日のような実在しない日付を拒否しません。入力値が実在する日付かどうかの検証は、呼び出し側の責務です。&lt;/p&gt;
&lt;h2&gt;定数探索&lt;/h2&gt;
&lt;p&gt;今回採用した式の形について、定数とシフト量も探索しました。&lt;/p&gt;
&lt;p&gt;100除算と400除算を同じ積から取得する次の形式では、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;product = year * C;

year / 100 = product &gt;&gt; shift;
year / 400 = product &gt;&gt; (shift + 2);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;年0〜9999の全域で成立する最小シフトは&lt;code&gt;19&lt;/code&gt;でした。&lt;/p&gt;
&lt;p&gt;その最小シフトにおける定数は、&lt;code&gt;5243&lt;/code&gt;のみでした。&lt;/p&gt;
&lt;p&gt;また、最終剰余の次の形式では、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;((value * C) &gt;&gt; shift) &amp;#x26; 7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;value&lt;/code&gt;の対象範囲全体で成立する最小シフトは&lt;code&gt;16&lt;/code&gt;でした。&lt;/p&gt;
&lt;p&gt;基本となる定数範囲内では、定数は&lt;code&gt;74899&lt;/code&gt;のみでした。&lt;/p&gt;
&lt;p&gt;これは、あくまで上記の式の形に限定した最小性です。&lt;/p&gt;
&lt;p&gt;あらゆるプログラムやstraight-line expressionの中で、この実装が最適であることを意味するものではありません。&lt;/p&gt;
&lt;h2&gt;ベンチマーク&lt;/h2&gt;
&lt;p&gt;Apple Silicon上で、Clangの次の条件を使って測定しました。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;-O3 -march=native
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ランダムな年、月、日をあらかじめ配列へ格納し、それを繰り返し処理しています。&lt;/p&gt;
&lt;p&gt;インライン・バッチ処理の結果は、おおむね次のとおりでした。&lt;/p&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;実装&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;時間&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;通常のSakamoto&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;約0.528 ns/件&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;最初のマジックナンバー版&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;約0.430 ns/件&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;月補正分配版&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;約0.370〜0.373 ns/件&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;通常のSakamoto実装に対する月補正分配版の改善率は、約29〜30%でした。&lt;/p&gt;
&lt;p&gt;scalar呼び出しでも高速化を確認しましたが、改善率はインライン化の有無、関数呼び出しの方法、コンパイラが生成する命令列によって変わります。&lt;/p&gt;
&lt;p&gt;また、Intel x86-64、Rust、Node.js、ブラウザJavaScriptなどでは、同じ改善率になるとは限りません。&lt;/p&gt;
&lt;p&gt;掲載したRust版とTypeScript版については、通常のSakamoto式との全件一致を確認しています。性能値を比較する場合は、対象となるCPU、コンパイラ、JITごとに測定する必要があります。&lt;/p&gt;
&lt;h2&gt;先行研究と今回の位置づけ&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;5243&lt;/code&gt;による0以上9999以下の整数の100除算は既知のstrength reductionです。&lt;/p&gt;
&lt;p&gt;例えば、整数から10進文字列への高速変換でも、次の形が使用されています。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;(value * 5243) &gt;&gt; 19
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;したがって、定数&lt;code&gt;5243&lt;/code&gt;自体を新しいものとして主張するものではありません。&lt;/p&gt;
&lt;p&gt;また、2026年8月17日にBen Joffe氏が公開した次の記事では、7が&lt;code&gt;2^3 - 1&lt;/code&gt;であることを利用し、限定範囲の剰余を乗算とシフトで求める原理が紹介されています。&lt;/p&gt;
&lt;p&gt;今回の実装のポイントは、これらの考え方を年1〜9999に限定したSakamoto式へ適用し、次の一つの実装へまとめたことです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;同じ積&lt;code&gt;year * 5243&lt;/code&gt;から&lt;code&gt;year / 100&lt;/code&gt;と&lt;code&gt;year / 400&lt;/code&gt;を取得する&lt;/li&gt;
&lt;li&gt;定数&lt;code&gt;74899&lt;/code&gt;によって最終的な&lt;code&gt;mod 7&lt;/code&gt;を3ビット抽出へ変換する&lt;/li&gt;
&lt;li&gt;月補正値を事前にスケーリングする&lt;/li&gt;
&lt;li&gt;月補正の加算を依存鎖の後段へ移動する&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;同じ定数構成とSakamoto式への適用については、調査した範囲では先行例を確認できていません。&lt;/p&gt;
&lt;p&gt;ただし、構成要素となる定数除算、strength reduction、限定範囲での剰余計算は既知の技法です。&lt;/p&gt;
&lt;h2&gt;まとめ&lt;/h2&gt;
&lt;p&gt;年を1〜9999に限定することで、Sakamotoの曜日計算に含まれる定数除算と&lt;code&gt;mod 7&lt;/code&gt;を、乗算、シフト、ビット抽出へ置き換えられました。&lt;/p&gt;
&lt;p&gt;主な変換は次の二つです。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;const uint32_t product = year * 5243u;

product &gt;&gt; 19; // year / 100
product &gt;&gt; 21; // year / 400
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;((value * 74899u) &gt;&gt; 16) &amp;#x26; 7u
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;さらに、月補正値をあらかじめ&lt;code&gt;74899&lt;/code&gt;倍することで、月項を年計算から続く依存鎖の後段へ移動しました。&lt;/p&gt;
&lt;p&gt;手元のApple Silicon環境では、Cのインライン・バッチ処理で通常のSakamoto実装より約30%高速になりました。&lt;/p&gt;
&lt;p&gt;一方、この実装には年1〜9999という適用範囲があります。また、性能差はCPUやコンパイラ、JITによって変化します。&lt;/p&gt;
&lt;p&gt;一般的なアプリケーションコードでは、読みやすく範囲制限のない通常のSakamoto式で十分です。今回の実装は、曜日計算がホットパスにあり、入力範囲が明確で、性能差を実測できる場合に向いています。&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>Faster Day-of-Week Calculation with Magic Numbers</title><link>https://veritycost.com/en/posts/fast-day-of-week-calc-magic-numbers-7aefe748/</link><guid isPermaLink="true">https://veritycost.com/en/posts/fast-day-of-week-calc-magic-numbers-7aefe748/</guid><description>An implementation, proof, and benchmark of a bounded optimization of Sakamoto&apos;s day-of-week algorithm using multiplication and bit shifts</description><pubDate>Mon, 24 Aug 2026 15:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Tomohiko Sakamoto’s method is a well-known algorithm for calculating the day of the week of a given date.&lt;/p&gt;
&lt;p&gt;This article restricts the supported year range to 1 through 9999 and replaces the constant divisions and modulo operation in Sakamoto’s calculation with multiplication, bit shifts, and bit extraction.&lt;/p&gt;
&lt;p&gt;In an inlined batch benchmark written in C and run on Apple Silicon, this implementation was approximately 30% faster than the ordinary Sakamoto implementation.&lt;/p&gt;
&lt;p&gt;I will first show the finished implementation and then explain why the constants &lt;code&gt;5243&lt;/code&gt; and &lt;code&gt;74899&lt;/code&gt; work.&lt;/p&gt;
&lt;h2&gt;Implementations&lt;/h2&gt;
&lt;h3&gt;TypeScript&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;/** Returns 0=Sunday, ..., 6=Saturday.
 *
 * Preconditions:
 * - year: 1..9999
 * - month: 1..12
 * - day: 1..31
 */
const SCALED_MONTH_TERM = new Uint32Array([
  0, 224_697, 149_798, 374_495, 0, 224_697, 374_495, 74_899, 299_596, 449_394, 149_798, 299_596,
]);

export function weekdayBoundedDistributed(year: number, month: number, day: number): number {
  year -= Number(month &amp;#x3C; 3);

  const product = Math.imul(year, 5_243);

  const base = year + (year &gt;&gt;&gt; 2) - (product &gt;&gt;&gt; 19) + (product &gt;&gt;&gt; 21) + day;

  return ((Math.imul(base, 74_899) + SCALED_MONTH_TERM[month - 1]) &gt;&gt;&gt; 16) &amp;#x26; 7;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The TypeScript implementation uses &lt;code&gt;Math.imul&lt;/code&gt; to explicitly perform 32-bit integer multiplication. It also uses the unsigned right-shift operator &lt;code&gt;&gt;&gt;&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Rust&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;/// Returns 0=Sunday, ..., 6=Saturday.
///
/// Preconditions:
/// - year: 1..9999
/// - month: 1..12
/// - day: 1..31
#[inline]
pub fn weekday_bounded_distributed(
    mut year: u32,
    month: u32,
    day: u32,
) -&gt; u32 {
    const SCALED_MONTH_TERM: [u32; 12] = [
        0, 224_697, 149_798, 374_495,
        0, 224_697, 374_495, 74_899,
        299_596, 449_394, 149_798, 299_596,
    ];

    year -= (month &amp;#x3C; 3) as u32;

    let product = year * 5_243;

    let base = year
        + (year &gt;&gt; 2)
        - (product &gt;&gt; 19)
        + (product &gt;&gt; 21)
        + day;

    (
        (
            base * 74_899 +
            SCALED_MONTH_TERM[(month - 1) as usize]
        ) &gt;&gt; 16
    ) &amp;#x26; 7
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;C&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;#include &amp;#x3C;stdint.h&gt;

/*
 * Returns 0=Sunday, ..., 6=Saturday.
 *
 * Preconditions:
 * - year: 1..9999
 * - month: 1..12
 * - day: 1..31
 */
static inline uint32_t weekday_bounded_distributed(
    uint32_t year,
    uint32_t month,
    uint32_t day
) {
    static const uint32_t scaled_month_term[12] = {
        0u, 224697u, 149798u, 374495u,
        0u, 224697u, 374495u, 74899u,
        299596u, 449394u, 149798u, 299596u
    };

    year -= month &amp;#x3C; 3;

    const uint32_t product = year * 5243u;

    const uint32_t base = year
        + (year &gt;&gt; 2)
        - (product &gt;&gt; 19)
        + (product &gt;&gt; 21)
        + day;

    return (
        (
            base * 74899u +
            scaled_month_term[month - 1]
        ) &gt;&gt; 16
    ) &amp;#x26; 7u;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The ordinary Sakamoto method&lt;/h2&gt;
&lt;p&gt;Sakamoto’s method uses the following month offsets:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A basic C implementation looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;static uint32_t weekday_sakamoto(
    uint32_t year,
    uint32_t month,
    uint32_t day
) {
    static const uint8_t month_term[12] = {
        0, 3, 2, 5, 0, 3,
        5, 1, 4, 6, 2, 4
    };

    year -= month &amp;#x3C; 3;

    return (
        year
        + year / 4
        - year / 100
        + year / 400
        + month_term[month - 1]
        + day
    ) % 7;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;January and February are treated as the thirteenth and fourteenth months of the previous year. Therefore, the year is decremented when &lt;code&gt;month &amp;#x3C; 3&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The algorithm then evaluates:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;year
+ floor(year / 4)
- floor(year / 100)
+ floor(year / 400)
+ month term
+ day
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The remainder after division by seven is the day of the week.&lt;/p&gt;
&lt;p&gt;The following page provides another explanation and implementation of Sakamoto’s method:&lt;/p&gt;
&lt;h2&gt;Replacing the divisions with magic numbers&lt;/h2&gt;
&lt;p&gt;This investigation was inspired by Falk Hüffner’s article:&lt;/p&gt;
&lt;p&gt;That article restricts the input range and transforms leap-year testing into a short expression involving multiplication, masking, and comparison.&lt;/p&gt;
&lt;p&gt;I wondered whether restricting the input range could similarly turn the constant divisions and remainder operation in Sakamoto’s formula into shorter integer operations.&lt;/p&gt;
&lt;p&gt;This implementation supports years from 1 through 9999.&lt;/p&gt;
&lt;p&gt;After decrementing the year for January and February, the adjusted year remains within the following range:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 &amp;#x3C;= year &amp;#x3C;= 9999
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This bounded range allows the following two transformations:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;const uint32_t product = year * 5243u;

product &gt;&gt; 19; // year / 100
product &gt;&gt; 21; // year / 400
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If &lt;code&gt;value&lt;/code&gt; is the intermediate result of Sakamoto’s expression, &lt;code&gt;value % 7&lt;/code&gt; can also be calculated as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;((value * 74899u) &gt;&gt; 16) &amp;#x26; 7u
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The two important constants are therefore:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5243
74899
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using &lt;code&gt;5243&lt;/code&gt; to replace division by 100 for inputs between 0 and 9999 is not new. It is a previously known optimization that replaces division by a constant with multiplication and a bit shift.&lt;/p&gt;
&lt;p&gt;One published example is:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.moria.us/blog/2023/01/optimizing-numtostring&quot;&gt;https://www.moria.us/blog/2023/01/optimizing-numtostring&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The implementation in this article additionally obtains both &lt;code&gt;year / 100&lt;/code&gt; and &lt;code&gt;year / 400&lt;/code&gt; from the same product, &lt;code&gt;year * 5243&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Distributing the month term&lt;/h2&gt;
&lt;p&gt;The first version of this implementation added the ordinary month term before multiplying the complete intermediate value by &lt;code&gt;74899&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;value =
    year_term
    + month_term[month - 1]
    + day;

weekday =
    ((value * 74899u) &gt;&gt; 16) &amp;#x26; 7u;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This was already faster than the ordinary Sakamoto implementation. However, the month-term addition remained in the dependency chain before the final multiplication.&lt;/p&gt;
&lt;p&gt;The multiplication can be distributed:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(year term + month term + day) * 74899

= (year term + day) * 74899
  + month term * 74899
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are only twelve month terms, so their scaled values can be stored directly.&lt;/p&gt;
&lt;p&gt;The ordinary month terms are:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After multiplication by &lt;code&gt;74899&lt;/code&gt;, they become:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0, 224697, 149798, 374495,
0, 224697, 374495, 74899,
299596, 449394, 149798, 299596
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This transformation moves the month-term addition after the final multiplication.&lt;/p&gt;
&lt;p&gt;On Apple Silicon, Clang compiled this part of the C implementation into a multiply-add instruction.&lt;/p&gt;
&lt;p&gt;The month table grows from 12 bytes to 48 bytes. On typical desktop and server processors, it still fits within a single cache line. In an environment where memory or code size is more important, the earlier implementation with the 12-byte unscaled month table can be used instead.&lt;/p&gt;
&lt;h2&gt;Why &lt;code&gt;5243&lt;/code&gt; performs division by 100&lt;/h2&gt;
&lt;p&gt;Let &lt;code&gt;year&lt;/code&gt; be the adjusted year.&lt;/p&gt;
&lt;p&gt;Its range is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 &amp;#x3C;= year &amp;#x3C;= 9999
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Write &lt;code&gt;year&lt;/code&gt; as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;year = 100 * q + r
0 &amp;#x3C;= r &amp;#x3C; 100
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5243 * 100
= 524300
= 2^19 + 12
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Therefore:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5243 * year

= 5243 * (100 * q + r)

= 2^19 * q
  + 12 * q
  + 5243 * r
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because &lt;code&gt;year &amp;#x3C;= 9999&lt;/code&gt;, both &lt;code&gt;q &amp;#x3C;= 99&lt;/code&gt; and &lt;code&gt;r &amp;#x3C;= 99&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The largest possible value of the remaining terms is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;12 * 99 + 5243 * 99
= 520245
&amp;#x3C; 2^19
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The expression &lt;code&gt;12 * q + 5243 * r&lt;/code&gt; therefore cannot carry into bit 19.&lt;/p&gt;
&lt;p&gt;Consequently:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;(year * 5243u) &gt;&gt; 19
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;is exactly equal to &lt;code&gt;year / 100&lt;/code&gt; throughout the supported range.&lt;/p&gt;
&lt;h2&gt;Why the same product performs division by 400&lt;/h2&gt;
&lt;p&gt;Now write &lt;code&gt;year&lt;/code&gt; as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;year = 400 * q + r
0 &amp;#x3C;= r &amp;#x3C; 400
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5243 * 400
= 2097200
= 2^21 + 48
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Therefore:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5243 * year

= 2^21 * q
  + 48 * q
  + 5243 * r
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because &lt;code&gt;year &amp;#x3C;= 9999&lt;/code&gt;, we have &lt;code&gt;q &amp;#x3C;= 24&lt;/code&gt; and &lt;code&gt;r &amp;#x3C;= 399&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The largest possible value of the remaining terms is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;48 * 24 + 5243 * 399
= 2093109
&amp;#x3C; 2^21
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These terms cannot carry into bit 21.&lt;/p&gt;
&lt;p&gt;Consequently:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;(year * 5243u) &gt;&gt; 21
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;is exactly equal to &lt;code&gt;year / 400&lt;/code&gt; throughout the supported range.&lt;/p&gt;
&lt;p&gt;A single multiplication can therefore provide both quotients:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;const uint32_t product = year * 5243u;

const uint32_t century = product &gt;&gt; 19;
const uint32_t four_centuries = product &gt;&gt; 21;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Why &lt;code&gt;74899&lt;/code&gt; calculates &lt;code&gt;mod 7&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Let &lt;code&gt;value&lt;/code&gt; be the intermediate result of Sakamoto’s expression.&lt;/p&gt;
&lt;p&gt;Under the stated input conditions:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 &amp;#x3C;= value &amp;#x3C; 13000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Write &lt;code&gt;value&lt;/code&gt; as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;value = 7 * q + r
0 &amp;#x3C;= r &amp;#x3C; 7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The constant &lt;code&gt;74899&lt;/code&gt; has the following properties:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;7 * 74899
= 8 * 2^16 + 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;74899
= 2^16 + 9363
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Expanding the multiplication gives:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;74899 * value

= 74899 * (7 * q + r)

= (8 * q + r) * 2^16
  + 5 * q
  + 9363 * r
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because &lt;code&gt;value &amp;#x3C; 13000&lt;/code&gt;, we have &lt;code&gt;q &amp;#x3C;= 1857&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The largest possible value of the remaining terms is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5 * 1857 + 9363 * 6
= 65463
&amp;#x3C; 2^16
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The expression &lt;code&gt;5 * q + 9363 * r&lt;/code&gt; therefore cannot carry into bit 16.&lt;/p&gt;
&lt;p&gt;Consequently:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(74899 * value) &gt;&gt; 16
= 8 * q + r
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The lowest three bits of &lt;code&gt;8 * q&lt;/code&gt; are always zero. Extracting the lowest three bits therefore leaves only &lt;code&gt;r&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;((value * 74899u) &gt;&gt; 16) &amp;#x26; 7u
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is exactly equal to &lt;code&gt;value % 7&lt;/code&gt; throughout the supported range.&lt;/p&gt;
&lt;h2&gt;No 32-bit overflow is required&lt;/h2&gt;
&lt;p&gt;This implementation does not depend on 32-bit integer overflow.&lt;/p&gt;
&lt;p&gt;The largest first product is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;9999 * 5243
= 52424757
&amp;#x3C; 2^32
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using the slightly wider bound &lt;code&gt;value &amp;#x3C; 13000&lt;/code&gt;, the largest final product is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;12999 * 74899
= 973612101
&amp;#x3C; 2^32
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All products fit within an unsigned 32-bit integer.&lt;/p&gt;
&lt;h2&gt;Exhaustive verification&lt;/h2&gt;
&lt;p&gt;Because the supported year range is finite, every input in the stated domain can be compared with the ordinary Sakamoto implementation.&lt;/p&gt;
&lt;p&gt;The following TypeScript program performs the exhaustive comparison:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;const MONTH_TERM = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4] as const;

function weekdaySakamoto(year: number, month: number, day: number): number {
  year -= Number(month &amp;#x3C; 3);

  return (
    (year +
      Math.floor(year / 4) -
      Math.floor(year / 100) +
      Math.floor(year / 400) +
      MONTH_TERM[month - 1] +
      day) %
    7
  );
}

for (let year = 1; year &amp;#x3C;= 9_999; year++) {
  for (let month = 1; month &amp;#x3C;= 12; month++) {
    for (let day = 1; day &amp;#x3C;= 31; day++) {
      const expected = weekdaySakamoto(year, month, day);

      const actual = weekdayBoundedDistributed(year, month, day);

      if (actual !== expected) {
        throw new Error(`mismatch: ${year}-${month}-${day}: ` + `${actual} !== ${expected}`);
      }
    }
  }
}

console.log(&quot;verified&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The number of compared inputs is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;9999 * 12 * 31
= 3,719,628
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The optimized implementation matched the ordinary Sakamoto formula for every year from 1 through 9999, every month from 1 through 12, and every day from 1 through 31.&lt;/p&gt;
&lt;p&gt;The test deliberately checks every day through 31 without considering the actual length of each month. It therefore verifies a larger input set than the set of valid calendar dates.&lt;/p&gt;
&lt;p&gt;The function itself does not reject nonexistent dates such as April 31. Date validation remains the caller’s responsibility.&lt;/p&gt;
&lt;h2&gt;Searching for the constants&lt;/h2&gt;
&lt;p&gt;I also searched the constants and shift amounts for the expression forms used by this implementation.&lt;/p&gt;
&lt;p&gt;For the shared-product form:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;product = year * C;

year / 100 = product &gt;&gt; shift;
year / 400 = product &gt;&gt; (shift + 2);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the smallest shift that works for every year from 0 through 9999 is &lt;code&gt;19&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;At that shift, &lt;code&gt;5243&lt;/code&gt; is the only valid constant.&lt;/p&gt;
&lt;p&gt;For the final remainder form:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;((value * C) &gt;&gt; shift) &amp;#x26; 7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the smallest shift that works throughout the required intermediate-value range is &lt;code&gt;16&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Within the fundamental multiplier range, &lt;code&gt;74899&lt;/code&gt; is the only valid constant.&lt;/p&gt;
&lt;p&gt;These are conditional minimality results. They apply to the two expression forms above and do not prove that this program is optimal among all possible straight-line programs.&lt;/p&gt;
&lt;p&gt;I also tested more aggressive forms that attempted to directly combine the year and month/day terms into one multiply-shift expression, but did not find a solution in the searched expression families.&lt;/p&gt;
&lt;h2&gt;Benchmark&lt;/h2&gt;
&lt;p&gt;The C benchmark was run on Apple Silicon with Clang using:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;-O3 -march=native
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Random years, months, and days were generated in advance and repeatedly processed from arrays.&lt;/p&gt;
&lt;p&gt;The approximate inlined batch results were:&lt;/p&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Implementation&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;Time&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Ordinary Sakamoto&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;0.528 ns/item&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Initial magic-number version&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;0.430 ns/item&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Distributed month-term version&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;0.370–0.373 ns/item&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The distributed version was approximately 29–30% faster than the ordinary Sakamoto implementation in this benchmark.&lt;/p&gt;
&lt;p&gt;The scalar version was also faster, although the improvement depends on inlining, calling conventions, compiler decisions, and the generated instruction sequence.&lt;/p&gt;
&lt;p&gt;The same percentage should not be assumed for Intel x86-64, Rust, Node.js, or browser JavaScript. The Rust and TypeScript implementations shown above were exhaustively verified for correctness, but performance should be measured independently on the target CPU, compiler, or JavaScript engine.&lt;/p&gt;
&lt;h2&gt;Prior work and the scope of this result&lt;/h2&gt;
&lt;p&gt;Replacing division by 100 with multiplication by &lt;code&gt;5243&lt;/code&gt; and a right shift is a known optimization for inputs between 0 and 9999.&lt;/p&gt;
&lt;p&gt;For example, the following expression appears in work on fast integer-to-decimal conversion:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;(value * 5243) &gt;&gt; 19
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;See:&lt;/p&gt;
&lt;p&gt;The constant &lt;code&gt;5243&lt;/code&gt; and the division-by-100 transformation are therefore not new.&lt;/p&gt;
&lt;p&gt;On August 17, 2026, Ben Joffe published an article describing how the fact that seven equals &lt;code&gt;2^3 - 1&lt;/code&gt; can be used to calculate a bounded remainder using multiplication and shifts:&lt;/p&gt;
&lt;p&gt;The general idea of replacing bounded constant division and remainder operations with multiplication and shifts is also established work.&lt;/p&gt;
&lt;p&gt;The contribution of the implementation in this article is the particular specialization of these ideas to Sakamoto’s formula for years 1 through 9999:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;obtain both &lt;code&gt;year / 100&lt;/code&gt; and &lt;code&gt;year / 400&lt;/code&gt; from the same product, &lt;code&gt;year * 5243&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;convert the final &lt;code&gt;mod 7&lt;/code&gt; into a three-bit extraction using &lt;code&gt;74899&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;pre-scale the month terms;&lt;/li&gt;
&lt;li&gt;move the month-term addition out of the critical dependency chain.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I did not find an earlier implementation using this same combination of constants and transformations for Sakamoto’s algorithm. This is not, however, a claim that the underlying multiplication-and-shift techniques are new.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;By restricting the supported year range to 1 through 9999, the constant divisions and final &lt;code&gt;mod 7&lt;/code&gt; in Sakamoto’s day-of-week calculation can be replaced with multiplication, shifts, and bit extraction.&lt;/p&gt;
&lt;p&gt;The two central transformations are:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;const uint32_t product = year * 5243u;

product &gt;&gt; 19; // year / 100
product &gt;&gt; 21; // year / 400
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;((value * 74899u) &gt;&gt; 16) &amp;#x26; 7u
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pre-scaling the month terms additionally moves the month-term addition out of the critical dependency chain.&lt;/p&gt;
&lt;p&gt;On my Apple Silicon system, the C implementation was approximately 30% faster than the ordinary Sakamoto implementation in an inlined batch benchmark.&lt;/p&gt;
&lt;p&gt;The implementation has an explicit year range of 1 through 9999, and performance depends on the CPU, compiler, JIT, and surrounding code.&lt;/p&gt;
&lt;p&gt;For most application code, the ordinary Sakamoto formula is simpler and sufficiently fast. This bounded implementation is most appropriate when day-of-week calculation is on a hot path, the input range is known, and the performance benefit has been measured on the target system.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This article is the English version of the original article written in Japanese, available &lt;a href=&quot;https://veritycost.com/posts/fast-day-of-week-calc-81ee505e/&quot;&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>A Faster days_from_civil Using Magic-Number Division</title><link>https://veritycost.com/en/posts/fast-days-from-civil-41f45490/</link><guid isPermaLink="true">https://veritycost.com/en/posts/fast-days-from-civil-41f45490/</guid><description>Restricting years to 1..9999 and replacing the constant divisions in days_from_civil with multiplication, bit shifts, and a month table</description><pubDate>Wed, 26 Aug 2026 12:51:20 GMT</pubDate><content:encoded>&lt;p&gt;Howard Hinnant’s &lt;code&gt;days_from_civil&lt;/code&gt; is a well-known algorithm for converting a civil year, month, and day into a day count relative to the Unix epoch.&lt;/p&gt;
&lt;p&gt;This article restricts the input years to 1 through 9999 and replaces the &lt;code&gt;/400&lt;/code&gt;, &lt;code&gt;/100&lt;/code&gt;, &lt;code&gt;/4&lt;/code&gt;, and &lt;code&gt;/5&lt;/code&gt; operations in &lt;code&gt;days_from_civil&lt;/code&gt; with two independent 32-bit integer multiplications, shifts, and a 12-element lookup table.&lt;/p&gt;
&lt;p&gt;In inline batch benchmarks on Apple Silicon, the bounded implementation reduced execution time by approximately 37–80% compared with the usual Hinnant implementation across Bun, Rust, and C in the tested environment.&lt;/p&gt;
&lt;p&gt;We will start with the complete implementations, then explain why &lt;code&gt;1461&lt;/code&gt; combines the common-year and four-year terms, why &lt;code&gt;5243&lt;/code&gt; implements both &lt;code&gt;/100&lt;/code&gt; and &lt;code&gt;/400&lt;/code&gt;, and how the epoch offset is folded into the month table.&lt;/p&gt;
&lt;h2&gt;Implementations&lt;/h2&gt;
&lt;p&gt;The preconditions are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;year&lt;/code&gt;: &lt;code&gt;1..9999&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;month&lt;/code&gt;: &lt;code&gt;0..11&lt;/code&gt;, where 0 is January&lt;/li&gt;
&lt;li&gt;&lt;code&gt;day&lt;/code&gt;: &lt;code&gt;1..31&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These functions do not validate whether the date actually exists. For example, February 31 is accepted as an arithmetic input.&lt;/p&gt;
&lt;h3&gt;TypeScript&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const EPOCH_MONTH_TABLE = new Int32Array([
  -719_163, -719_132, -719_469, -719_438,
  -719_408, -719_377, -719_347, -719_316,
  -719_285, -719_255, -719_224, -719_194,
]);

/** Returns days since 1970-01-01. */
export function ymdToEpochDays(
  year: number,
  month: number,
  day: number,
): number {
  const adjustedYear = year - (month &amp;#x3C;= 1 ? 1 : 0);
  const centuryProduct = Math.imul(adjustedYear, 5_243);

  return (
    (Math.imul(adjustedYear, 1_461) &gt;&gt;&gt; 2) -
    (centuryProduct &gt;&gt;&gt; 19) +
    (centuryProduct &gt;&gt;&gt; 21) +
    EPOCH_MONTH_TABLE[month] +
    day
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;TypeScript uses &lt;code&gt;Math.imul&lt;/code&gt; to request 32-bit integer multiplication explicitly. All shifted values are nonnegative under the preconditions, so unsigned right shift, &lt;code&gt;&gt;&gt;&gt;&lt;/code&gt;, is used.&lt;/p&gt;
&lt;h3&gt;Rust&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;const EPOCH_MONTH_TABLE: [i32; 12] = [
    -719_163, -719_132, -719_469, -719_438,
    -719_408, -719_377, -719_347, -719_316,
    -719_285, -719_255, -719_224, -719_194,
];

/// Returns days since 1970-01-01.
///
/// Preconditions:
/// - year: 1..=9999
/// - month: 0..=11
/// - day: 1..=31
#[inline]
pub fn ymd_to_epoch_days(year: u32, month: u32, day: u32) -&gt; i32 {
    let adjusted_year = year - u32::from(month &amp;#x3C;= 1);
    let century_product = adjusted_year * 5_243;

    ((adjusted_year * 1_461) &gt;&gt; 2) as i32
        - (century_product &gt;&gt; 19) as i32
        + (century_product &gt;&gt; 21) as i32
        + EPOCH_MONTH_TABLE[month as usize]
        + day as i32
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;C&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;#include &amp;#x3C;stdint.h&gt;

/*
 * Returns days since 1970-01-01.
 *
 * Preconditions:
 * - year: 1..9999
 * - month: 0..11
 * - day: 1..31
 */
static inline int32_t ymd_to_epoch_days(
    uint32_t year,
    uint32_t month,
    uint32_t day
) {
    static const int32_t epoch_month_table[12] = {
        -719163, -719132, -719469, -719438,
        -719408, -719377, -719347, -719316,
        -719285, -719255, -719224, -719194
    };

    const uint32_t adjusted_year = year - (month &amp;#x3C;= 1u);
    const uint32_t century_product = adjusted_year * 5243u;

    return (int32_t)((adjusted_year * 1461u) &gt;&gt; 2)
        - (int32_t)(century_product &gt;&gt; 19)
        + (int32_t)(century_product &gt;&gt; 21)
        + epoch_month_table[month]
        + (int32_t)day;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The Usual &lt;code&gt;days_from_civil&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Howard Hinnant’s implementation, adapted to zero-based months, can be written as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function daysFromCivil(year: number, month: number, day: number): number {
  year -= month &amp;#x3C;= 1 ? 1 : 0;

  const era = Math.floor(year / 400);
  const yearOfEra = year - era * 400;
  const marchMonth = month + (month &gt; 1 ? -2 : 10);
  const dayOfYear =
    Math.floor((153 * marchMonth + 2) / 5) + day - 1;
  const dayOfEra =
    yearOfEra * 365 +
    Math.floor(yearOfEra / 4) -
    Math.floor(yearOfEra / 100) +
    dayOfYear;

  return era * 146_097 + dayOfEra - 719_468;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;January and February are treated as the final two months of the preceding computational year. This moves the leap day to the end of that year. The calculation then splits the date into a 400-year era, a year within the era, and a day counted from March 1.&lt;/p&gt;
&lt;h2&gt;The Transformations&lt;/h2&gt;
&lt;p&gt;There are three main transformations.&lt;/p&gt;
&lt;p&gt;First, the common-year contribution and the &lt;code&gt;/4&lt;/code&gt; leap-year contribution are combined:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;Math.imul(adjustedYear, 1_461) &gt;&gt;&gt; 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Second, &lt;code&gt;/100&lt;/code&gt; and &lt;code&gt;/400&lt;/code&gt; are obtained from one shared product:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const product = Math.imul(adjustedYear, 5_243);

product &gt;&gt;&gt; 19; // adjustedYear / 100
product &gt;&gt;&gt; 21; // adjustedYear / 400
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Third, the month contribution, &lt;code&gt;-1&lt;/code&gt;, and the Unix epoch offset are folded into one lookup table.&lt;/p&gt;
&lt;p&gt;The number of days before each month has only 12 possible values, so &lt;code&gt;(153 * month + 2) / 5&lt;/code&gt; is replaced with a lookup table.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const EPOCH_MONTH_TABLE = new Int32Array([
  -719_163, -719_132, -719_469, -719_438,
  -719_408, -719_377, -719_347, -719_316,
  -719_285, -719_255, -719_224, -719_194,
]);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Combining the 365-Day and Four-Year Terms&lt;/h2&gt;
&lt;p&gt;The year contribution begins with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;365 * adjustedYear + floor(adjustedYear / 4)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since &lt;code&gt;1461 = 4 * 365 + 1&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;floor(1461 * adjustedYear / 4)
= 365 * adjustedYear + floor(adjustedYear / 4)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The input is nonnegative, so the division by four becomes an unsigned shift:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;Math.imul(adjustedYear, 1_461) &gt;&gt;&gt; 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This replaces a multiplication, an independent shift, and their addition with one multiplication followed by a shift.&lt;/p&gt;
&lt;h2&gt;Folding the Epoch Offset into the Month Table&lt;/h2&gt;
&lt;p&gt;The original tail of the expression is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;DOY_TABLE[month] + day - 1 - 719468
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The constant terms can be precomputed for each month:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;EPOCH_MONTH_TABLE[month]
= DOY_TABLE[month] - 719469
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The runtime expression then becomes only:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;EPOCH_MONTH_TABLE[month] + day
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example, January uses &lt;code&gt;306 - 719469 = -719163&lt;/code&gt;. The table remains 48 bytes; only its values change.&lt;/p&gt;
&lt;h2&gt;Why &lt;code&gt;5243&lt;/code&gt; Implements Division by 100&lt;/h2&gt;
&lt;p&gt;After the January/February adjustment, the range is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 &amp;#x3C;= adjustedYear &amp;#x3C;= 9999
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Write the adjusted year as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;adjustedYear = 100q + r
0 &amp;#x3C;= r &amp;#x3C; 100
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5243 * 100
= 524300
= 2^19 + 12
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Therefore:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5243 * adjustedYear
= 2^19 * q + 12q + 5243r
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Within the input range, &lt;code&gt;q &amp;#x3C;= 99&lt;/code&gt; and &lt;code&gt;r &amp;#x3C;= 99&lt;/code&gt;. The maximum value of the remaining part is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;12 * 99 + 5243 * 99
= 520245
&amp;#x3C; 2^19
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It cannot carry into bit 19. Consequently:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;Math.imul(adjustedYear, 5243) &gt;&gt;&gt; 19
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;is exactly &lt;code&gt;floor(adjustedYear / 100)&lt;/code&gt; throughout the bounded range.&lt;/p&gt;
&lt;h2&gt;Why the Same Product Implements Division by 400&lt;/h2&gt;
&lt;p&gt;Now write:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;adjustedYear = 400q + r
0 &amp;#x3C;= r &amp;#x3C; 400
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also have:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5243 * 400
= 2097200
= 2^21 + 48
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Thus:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5243 * adjustedYear
= 2^21 * q + 48q + 5243r
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here &lt;code&gt;q &amp;#x3C;= 24&lt;/code&gt; and &lt;code&gt;r &amp;#x3C;= 399&lt;/code&gt;. The maximum remaining part is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;48 * 24 + 5243 * 399
= 2093109
&amp;#x3C; 2^21
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It cannot carry into bit 21. Therefore:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;Math.imul(adjustedYear, 5243) &gt;&gt;&gt; 21
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;is exactly &lt;code&gt;floor(adjustedYear / 400)&lt;/code&gt; throughout the range.&lt;/p&gt;
&lt;p&gt;One multiplication can consequently be shared:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const product = Math.imul(adjustedYear, 5243);
const century = product &gt;&gt;&gt; 19;
const era = product &gt;&gt;&gt; 21;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Replacing the Month Division with a Table&lt;/h2&gt;
&lt;p&gt;The March-based calendar uses:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;floor((153 * marchMonth + 2) / 5)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;marchMonth&lt;/code&gt; has only 12 possible values. Expanded and reordered for zero-based Gregorian months, those values are:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
306 337   0  31  61  92 122 153 184 214 245 275
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The lookup replaces the month adjustment branch, multiplication, addition, and &lt;code&gt;/5&lt;/code&gt;. The final implementation additionally stores each value after subtracting &lt;code&gt;719469&lt;/code&gt;, folding both the Unix epoch offset and the day-index correction into the same table.&lt;/p&gt;
&lt;p&gt;The table occupies 48 bytes. That is small on ordinary PCs and servers, but the relative performance of a table and arithmetic can still depend on cold-cache behavior, code size, bounds checks, and JIT optimization.&lt;/p&gt;
&lt;h2&gt;32-Bit Integer Bounds&lt;/h2&gt;
&lt;p&gt;Using the inclusive upper bound of 9999, the largest products are:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;9999 * 5243
= 52424757
&amp;#x3C; 2^31

9999 * 1461
= 14608539
&amp;#x3C; 2^31
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The TypeScript implementation therefore does not wrap a signed 32-bit &lt;code&gt;Math.imul&lt;/code&gt; result.&lt;/p&gt;
&lt;p&gt;All intermediate values fit comfortably in 32 bits. Across the complete specified input set, the output range was:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;-719162 &amp;#x3C;= result &amp;#x3C;= 2932896
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exhaustive Verification&lt;/h2&gt;
&lt;p&gt;Every input in the bounded domain was compared with the usual Hinnant implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;for (let year = 1; year &amp;#x3C;= 9_999; year++) {
  for (let month = 0; month &amp;#x3C; 12; month++) {
    for (let day = 1; day &amp;#x3C;= 31; day++) {
      const expected = daysFromCivil(year, month, day);
      const actual = ymdToEpochDays(year, month, day);

      if (actual !== expected) {
        throw new Error(
          `mismatch: ${year}-${month}-${day}: ` +
          `${actual} !== ${expected}`,
        );
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The number of comparisons was:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;9999 * 12 * 31
= 3,719,628
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The TypeScript, Rust, and C implementations all matched across the complete set.&lt;/p&gt;
&lt;p&gt;The test intentionally checks every &lt;code&gt;day&lt;/code&gt; through 31 without accounting for the actual length of each month. It therefore includes nonexistent dates such as February 31. The conversion remains arithmetically consistent, but callers are responsible for validating real civil dates.&lt;/p&gt;
&lt;h2&gt;Benchmarks&lt;/h2&gt;
&lt;p&gt;The benchmarks were run on Apple Silicon ARM64. Each implementation processed a pre-generated array of &lt;code&gt;2^20&lt;/code&gt; dates 32 times. The input position was rotated between rounds to prevent an optimizer from hoisting an identical loop.&lt;/p&gt;
&lt;p&gt;Inputs were generated with a deterministic pseudorandom sequence over:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;year: &lt;code&gt;1..9999&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;month: &lt;code&gt;0..11&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;day: &lt;code&gt;1..28&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The best result from seven trials was recorded. The toolchain was:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Bun 1.4.0
rustc 1.96.0 -C opt-level=3 -C target-cpu=native
Apple clang 21.0.0 -O3 -march=native
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Results:&lt;/p&gt;

































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Environment&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;Usual Hinnant&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;Bounded version&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;Time reduction&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;Throughput&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Bun&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;14.738 ns/item&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;3.018 ns/item&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;79.5%&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;4.88x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Rust&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;2.001 ns/item&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1.192 ns/item&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;40.4%&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1.68x&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;C&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1.923 ns/item&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1.205 ns/item&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;37.3%&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1.60x&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The effect is especially large in Bun, where ordinary floating-point division plus &lt;code&gt;Math.floor&lt;/code&gt; is replaced with &lt;code&gt;Math.imul&lt;/code&gt; and shifts.&lt;/p&gt;
&lt;p&gt;C and Rust compilers can already lower division by constants to multiplication and shifts. Even so, combining the 365-day and &lt;code&gt;/4&lt;/code&gt; terms, sharing one product between &lt;code&gt;/100&lt;/code&gt; and &lt;code&gt;/400&lt;/code&gt;, and folding the remaining constants into the month table reduced execution time by about 37–40% in this environment.&lt;/p&gt;
&lt;p&gt;These results apply to one CPU, compiler/JIT version, and input distribution. Intel x86-64, other Apple Silicon generations, Node.js/V8, browsers, and different Rust or Clang versions may produce different results.&lt;/p&gt;
&lt;h2&gt;Prior Work and Positioning&lt;/h2&gt;
&lt;p&gt;This implementation is based on Howard Hinnant’s &lt;code&gt;days_from_civil&lt;/code&gt;. Strength reduction from division by constants to multiplication and shifts is also well established.&lt;/p&gt;
&lt;p&gt;Division by 100 using &lt;code&gt;5243&lt;/code&gt; over integers from 0 through 9999 is itself known:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(value * 5243) &gt;&gt; 19
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The construction here applies the bounded shared-product technique from the earlier article, “A Faster Day-of-Week Calculation Using Magic Numbers,” to &lt;code&gt;days_from_civil&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The relevant combination is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;obtain &lt;code&gt;/100&lt;/code&gt; and &lt;code&gt;/400&lt;/code&gt; from the same &lt;code&gt;adjustedYear * 5243&lt;/code&gt; product;&lt;/li&gt;
&lt;li&gt;combine &lt;code&gt;365 * adjustedYear + floor(adjustedYear / 4)&lt;/code&gt; as &lt;code&gt;floor(1461 * adjustedYear / 4)&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;replace the March-based month expression with a 12-element table that also contains the epoch offset;&lt;/li&gt;
&lt;li&gt;use explicit &lt;code&gt;Math.imul&lt;/code&gt; and unsigned shifts in JavaScript; and&lt;/li&gt;
&lt;li&gt;complete the calculation in 32 bits by restricting years to &lt;code&gt;1..9999&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Neri and Schneider systematically study multiplication-and-shift Gregorian calendar conversion and dependency-chain reduction through Euclidean affine functions. The individual ingredients and the general optimization strategy are therefore not claimed as new.&lt;/p&gt;
&lt;p&gt;Within the public implementations examined, however, I did not find the same bounded &lt;code&gt;days_from_civil&lt;/code&gt; construction combining the &lt;code&gt;1461&lt;/code&gt; year product, one &lt;code&gt;5243&lt;/code&gt; product for both &lt;code&gt;/100&lt;/code&gt; and &lt;code&gt;/400&lt;/code&gt;, and this epoch-adjusted month table.&lt;/p&gt;
&lt;p&gt;This does not establish optimality across all expressions, languages, CPUs, or input domains. It is a target-specific optimization for a clearly bounded year range.&lt;/p&gt;
&lt;h2&gt;The Inverse Conversion&lt;/h2&gt;
&lt;p&gt;This article covers &lt;code&gt;days_from_civil&lt;/code&gt;, which converts a year, month, and day into a serial day number. The inverse operation is normally called &lt;code&gt;civil_from_days&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;civil date -- days_from_civil --&gt; serial day
civil date &amp;#x3C;-- civil_from_days --- serial day
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Although the two functions are inverses, their optimization structures differ substantially. &lt;code&gt;civil_from_days&lt;/code&gt; must recover the era, year, month, and day from one integer, and it uses different divisions, corrections, and magic constants. It is therefore best treated separately.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Restricting the year to &lt;code&gt;1..9999&lt;/code&gt; allows the constant divisions in Hinnant-style &lt;code&gt;days_from_civil&lt;/code&gt; to be replaced with two independent multiplications and shifts.&lt;/p&gt;
&lt;p&gt;The central transformations are:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;Math.imul(adjustedYear, 1_461) &gt;&gt;&gt; 2;

const centuryProduct = Math.imul(adjustedYear, 5_243);

centuryProduct &gt;&gt;&gt; 19; // adjustedYear / 100
centuryProduct &gt;&gt;&gt; 21; // adjustedYear / 400
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;EPOCH_MONTH_TABLE[month] + day;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the Apple Silicon benchmarks reported here, the bounded version reduced execution time by approximately 80% in Bun and 37–40% in Rust and C compared with the usual Hinnant implementation.&lt;/p&gt;
&lt;p&gt;For ordinary application code, the clearer and broadly ranged &lt;code&gt;days_from_civil&lt;/code&gt; is generally sufficient. This bounded implementation is intended for cases where civil-date conversion is on a hot path, the input range is guaranteed, and the performance difference has been measured on the target environment.&lt;/p&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Howard Hinnant, &lt;a href=&quot;https://howardhinnant.github.io/date_algorithms.html&quot;&gt;chrono-Compatible Low-Level Date Algorithms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Cassio Neri and Lorenz Schneider, &lt;a href=&quot;https://arxiv.org/abs/2102.06959&quot;&gt;Euclidean Affine Functions and Applications to Calendar Algorithms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Cassio Neri, &lt;a href=&quot;https://github.com/cassioneri/eaf&quot;&gt;EAF supplementary material&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://veritycost.com/posts/fast-day-of-week-calc-81ee505e/&quot;&gt;A Faster Day-of-Week Calculation Using Magic Numbers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>マジックナンバー化による高速な days_from_civil</title><link>https://veritycost.com/posts/fast-days-from-civil-magic-numbers-f7f1436a/</link><guid isPermaLink="true">https://veritycost.com/posts/fast-days-from-civil-magic-numbers-f7f1436a/</guid><description>year を 1..9999 に制限し、days_from_civil 内の定数除算を乗算・シフト・テーブル参照に置き換えて高速化する</description><pubDate>Wed, 26 Aug 2026 12:51:20 GMT</pubDate><content:encoded>&lt;p&gt;年月日をUnix epochからの通算日へ変換する方法として、Howard Hinnantの &lt;code&gt;days_from_civil&lt;/code&gt; がよく知られています。&lt;/p&gt;
&lt;p&gt;この記事では対象年を1年から9999年に限定し、&lt;code&gt;days_from_civil&lt;/code&gt; に含まれる &lt;code&gt;/400&lt;/code&gt;、&lt;code&gt;/100&lt;/code&gt;、&lt;code&gt;/4&lt;/code&gt;、&lt;code&gt;/5&lt;/code&gt; を、互いに独立した二つの32-bit整数乗算、シフト、12要素のテーブルに置き換えます。&lt;/p&gt;
&lt;p&gt;Apple Silicon上でBun、Rust、Cのインライン・バッチ処理を測定したところ、通常のHinnant実装に対して、今回の環境では約37〜80%の実行時間短縮を確認しました。&lt;/p&gt;
&lt;p&gt;最初に完成した実装を示し、その後で &lt;code&gt;1461&lt;/code&gt; が平年項と4年項をまとめられる理由、定数 &lt;code&gt;5243&lt;/code&gt; が &lt;code&gt;/100&lt;/code&gt; と &lt;code&gt;/400&lt;/code&gt; の両方に使える理由、epochオフセットを月テーブルへ畳み込む方法を説明します。&lt;/p&gt;
&lt;h2&gt;実装例&lt;/h2&gt;
&lt;p&gt;前提は次のとおりです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;year&lt;/code&gt;: &lt;code&gt;1..9999&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;month&lt;/code&gt;: &lt;code&gt;0..11&lt;/code&gt;（0が1月）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;day&lt;/code&gt;: &lt;code&gt;1..31&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;この関数は日付の妥当性を検証しません。たとえば2月31日も計算できます。&lt;/p&gt;
&lt;h3&gt;TypeScript&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const EPOCH_MONTH_TABLE = new Int32Array([
  -719_163, -719_132, -719_469, -719_438,
  -719_408, -719_377, -719_347, -719_316,
  -719_285, -719_255, -719_224, -719_194,
]);

/** Returns days since 1970-01-01. */
export function ymdToEpochDays(
  year: number,
  month: number,
  day: number,
): number {
  const adjustedYear = year - (month &amp;#x3C;= 1 ? 1 : 0);
  const centuryProduct = Math.imul(adjustedYear, 5_243);

  return (
    (Math.imul(adjustedYear, 1_461) &gt;&gt;&gt; 2) -
    (centuryProduct &gt;&gt;&gt; 19) +
    (centuryProduct &gt;&gt;&gt; 21) +
    EPOCH_MONTH_TABLE[month] +
    day
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;TypeScriptでは32-bit整数乗算を明示するために &lt;code&gt;Math.imul&lt;/code&gt; を使います。対象値は常に非負なので、右シフトには符号なしの &lt;code&gt;&gt;&gt;&gt;&lt;/code&gt; を使用しています。&lt;/p&gt;
&lt;h3&gt;Rust&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;const EPOCH_MONTH_TABLE: [i32; 12] = [
    -719_163, -719_132, -719_469, -719_438,
    -719_408, -719_377, -719_347, -719_316,
    -719_285, -719_255, -719_224, -719_194,
];

/// Returns days since 1970-01-01.
///
/// Preconditions:
/// - year: 1..=9999
/// - month: 0..=11
/// - day: 1..=31
#[inline]
pub fn ymd_to_epoch_days(year: u32, month: u32, day: u32) -&gt; i32 {
    let adjusted_year = year - u32::from(month &amp;#x3C;= 1);
    let century_product = adjusted_year * 5_243;

    ((adjusted_year * 1_461) &gt;&gt; 2) as i32
        - (century_product &gt;&gt; 19) as i32
        + (century_product &gt;&gt; 21) as i32
        + EPOCH_MONTH_TABLE[month as usize]
        + day as i32
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;C&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;#include &amp;#x3C;stdint.h&gt;

/*
 * Returns days since 1970-01-01.
 *
 * Preconditions:
 * - year: 1..9999
 * - month: 0..11
 * - day: 1..31
 */
static inline int32_t ymd_to_epoch_days(
    uint32_t year,
    uint32_t month,
    uint32_t day
) {
    static const int32_t epoch_month_table[12] = {
        -719163, -719132, -719469, -719438,
        -719408, -719377, -719347, -719316,
        -719285, -719255, -719224, -719194
    };

    const uint32_t adjusted_year = year - (month &amp;#x3C;= 1u);
    const uint32_t century_product = adjusted_year * 5243u;

    return (int32_t)((adjusted_year * 1461u) &gt;&gt; 2)
        - (int32_t)(century_product &gt;&gt; 19)
        + (int32_t)(century_product &gt;&gt; 21)
        + epoch_month_table[month]
        + (int32_t)day;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;通常の &lt;code&gt;days_from_civil&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Howard Hinnantの実装を、0始まりの月へ合わせて書くと次のようになります。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function daysFromCivil(year: number, month: number, day: number): number {
  year -= month &amp;#x3C;= 1 ? 1 : 0;

  const era = Math.floor(year / 400);
  const yearOfEra = year - era * 400;
  const marchMonth = month + (month &gt; 1 ? -2 : 10);
  const dayOfYear =
    Math.floor((153 * marchMonth + 2) / 5) + day - 1;
  const dayOfEra =
    yearOfEra * 365 +
    Math.floor(yearOfEra / 4) -
    Math.floor(yearOfEra / 100) +
    dayOfYear;

  return era * 146_097 + dayOfEra - 719_468;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;1月と2月を前年の末尾として扱うことで、うるう日を計算年の最後へ移します。その後、400年周期、周期内の年、3月から数えた年内日へ分解します。&lt;/p&gt;
&lt;h2&gt;今回の変形&lt;/h2&gt;
&lt;p&gt;最適化は大きく三つです。&lt;/p&gt;
&lt;p&gt;まず、平年の日数と &lt;code&gt;/4&lt;/code&gt; のうるう年項をまとめます。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;Math.imul(adjustedYear, 1_461) &gt;&gt;&gt; 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;次に、一度の乗算から &lt;code&gt;/100&lt;/code&gt; と &lt;code&gt;/400&lt;/code&gt; の両方を取得します。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const product = Math.imul(adjustedYear, 5_243);

product &gt;&gt;&gt; 19; // adjustedYear / 100
product &gt;&gt;&gt; 21; // adjustedYear / 400
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最後に、月項、&lt;code&gt;-1&lt;/code&gt;、Unix epochオフセットを一つのテーブルへ畳み込みます。月初までの日数は12通りしかないため、&lt;code&gt;(153 * month + 2) / 5&lt;/code&gt; も同時にテーブルへ置き換えられます。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const EPOCH_MONTH_TABLE = new Int32Array([
  -719_163, -719_132, -719_469, -719_438,
  -719_408, -719_377, -719_347, -719_316,
  -719_285, -719_255, -719_224, -719_194,
]);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;365日項と4年項を一つにする&lt;/h2&gt;
&lt;p&gt;年に関する先頭部分は次の形です。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;365 * adjustedYear + floor(adjustedYear / 4)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;1461 = 4 * 365 + 1&lt;/code&gt; なので、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;floor(1461 * adjustedYear / 4)
= 365 * adjustedYear + floor(adjustedYear / 4)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;となります。入力は非負なので、4除算は符号なし右シフトにできます。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;Math.imul(adjustedYear, 1_461) &gt;&gt;&gt; 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これにより、乗算、独立したシフト、その加算を、一つの乗算とシフトへ置き換えられます。&lt;/p&gt;
&lt;h2&gt;epochオフセットを月テーブルへ畳み込む&lt;/h2&gt;
&lt;p&gt;元の式の末尾は次の形です。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;DOY_TABLE[month] + day - 1 - 719468
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;月ごとに変わらない定数部分をあらかじめ計算します。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;EPOCH_MONTH_TABLE[month]
= DOY_TABLE[month] - 719469
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;実行時の末尾は次だけになります。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;EPOCH_MONTH_TABLE[month] + day
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;たとえば1月は &lt;code&gt;306 - 719469 = -719163&lt;/code&gt; です。テーブルの大きさは48バイトのままで、格納する値だけが変わります。&lt;/p&gt;
&lt;h2&gt;なぜ &lt;code&gt;5243&lt;/code&gt; で100除算できるのか&lt;/h2&gt;
&lt;p&gt;1月と2月の調整後も、年は次の範囲です。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 &amp;#x3C;= adjustedYear &amp;#x3C;= 9999
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;少し広く &lt;code&gt;adjustedYear &amp;#x3C;= 9999&lt;/code&gt; として証明します。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;adjustedYear = 100q + r
0 &amp;#x3C;= r &amp;#x3C; 100
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;また、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5243 * 100
= 524300
= 2^19 + 12
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;なので、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5243 * adjustedYear
= 2^19 * q + 12q + 5243r
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;です。対象範囲では &lt;code&gt;q &amp;#x3C;= 99&lt;/code&gt;、&lt;code&gt;r &amp;#x3C;= 99&lt;/code&gt; であり、余分な部分の最大値は、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;12 * 99 + 5243 * 99
= 520245
&amp;#x3C; 2^19
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;となります。19ビット目への桁上がりがないため、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;(Math.imul(adjustedYear, 5243) &gt;&gt;&gt; 19)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;は対象範囲で常に &lt;code&gt;floor(adjustedYear / 100)&lt;/code&gt; と一致します。&lt;/p&gt;
&lt;h2&gt;同じ積で400除算できる理由&lt;/h2&gt;
&lt;p&gt;今度は、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;adjustedYear = 400q + r
0 &amp;#x3C;= r &amp;#x3C; 400
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;とします。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5243 * 400
= 2097200
= 2^21 + 48
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;なので、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5243 * adjustedYear
= 2^21 * q + 48q + 5243r
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;です。&lt;code&gt;q &amp;#x3C;= 24&lt;/code&gt;、&lt;code&gt;r &amp;#x3C;= 399&lt;/code&gt; より、余分な部分は最大でも、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;48 * 24 + 5243 * 399
= 2093109
&amp;#x3C; 2^21
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;です。したがって、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;(Math.imul(adjustedYear, 5243) &gt;&gt;&gt; 21)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;は対象範囲で常に &lt;code&gt;floor(adjustedYear / 400)&lt;/code&gt; と一致します。&lt;/p&gt;
&lt;p&gt;以上から、一度の乗算結果を共有できます。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const product = Math.imul(adjustedYear, 5243);
const century = product &gt;&gt;&gt; 19;
const era = product &gt;&gt;&gt; 21;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;月の除算をテーブルへ置き換える&lt;/h2&gt;
&lt;p&gt;March-based calendarで使われる月項は次の式です。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;floor((153 * marchMonth + 2) / 5)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;marchMonth&lt;/code&gt; は &lt;code&gt;0..11&lt;/code&gt; の12値しか取りません。元の0始まり月へ対応させて展開すると、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
306 337   0  31  61  92 122 153 184 214 245 275
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;です。&lt;/p&gt;
&lt;p&gt;このテーブルにより、月の分岐、乗算、加算、&lt;code&gt;/5&lt;/code&gt; を一つのテーブル読み出しへ置き換えられます。最終実装ではさらに各値から &lt;code&gt;719469&lt;/code&gt; を引き、Unix epochオフセットと日番号の補正も同じテーブルへ畳み込んでいます。&lt;/p&gt;
&lt;p&gt;テーブルは48バイトです。一般的なPCでは小さいですが、cold cache、コードサイズ、bounds check、JITの扱いによって算術式との性能関係は変わります。&lt;/p&gt;
&lt;h2&gt;32-bit整数の範囲&lt;/h2&gt;
&lt;p&gt;広い上限 &lt;code&gt;9999&lt;/code&gt; を使った二つの積の最大値は、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;9999 * 5243
= 52424757
&amp;#x3C; 2^31

9999 * 1461
= 14608539
&amp;#x3C; 2^31
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;です。したがって、TypeScriptの &lt;code&gt;Math.imul&lt;/code&gt; で符号付き32-bitの折り返しは発生しません。&lt;/p&gt;
&lt;p&gt;その他の中間値も32-bit整数へ十分収まります。最終結果は、指定された入力集合で次の範囲でした。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;-719162 &amp;#x3C;= result &amp;#x3C;= 2932896
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;全入力での検証&lt;/h2&gt;
&lt;p&gt;対象となるすべての入力について、通常のHinnant実装と比較しました。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;for (let year = 1; year &amp;#x3C;= 9_999; year++) {
  for (let month = 0; month &amp;#x3C; 12; month++) {
    for (let day = 1; day &amp;#x3C;= 31; day++) {
      const expected = daysFromCivil(year, month, day);
      const actual = ymdToEpochDays(year, month, day);

      if (actual !== expected) {
        throw new Error(
          `mismatch: ${year}-${month}-${day}: ` +
          `${actual} !== ${expected}`,
        );
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;比較した入力数は、&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;9999 * 12 * 31
= 3,719,628
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;です。TypeScript、Rust、Cの各実装で全件一致しました。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;day&lt;/code&gt; は各月の実際の日数によらず31まで検証しています。そのため、2月31日のような実在しない日付も含まれます。関数は入力を拒否せず、通算日として連続的に計算します。実在する日付かどうかの検証は呼び出し側の責務です。&lt;/p&gt;
&lt;h2&gt;ベンチマーク&lt;/h2&gt;
&lt;p&gt;Apple Silicon ARM64上で、事前生成した &lt;code&gt;2^20&lt;/code&gt; 件の年月日を32回処理しました。入力位置はラウンドごとに回転させ、同一ループの巻き上げを防いでいます。&lt;/p&gt;
&lt;p&gt;入力は次の範囲から決定的な疑似乱数で生成しました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;年: &lt;code&gt;1..9999&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;月: &lt;code&gt;0..11&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;日: &lt;code&gt;1..28&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;7回測定した最小値を採用しています。コンパイル・実行条件は次のとおりです。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Bun 1.4.0
rustc 1.96.0 -C opt-level=3 -C target-cpu=native
Apple clang 21.0.0 -O3 -march=native
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;結果は次のとおりでした。&lt;/p&gt;

































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;環境&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;通常のHinnant&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;今回の限定範囲版&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;時間短縮&lt;/th&gt;&lt;th align=&quot;right&quot;&gt;スループット&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Bun&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;14.738 ns/件&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;3.018 ns/件&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;79.5%&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;4.88倍&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Rust&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;2.001 ns/件&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1.192 ns/件&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;40.4%&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1.68倍&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;C&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1.923 ns/件&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1.205 ns/件&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;37.3%&lt;/td&gt;&lt;td align=&quot;right&quot;&gt;1.60倍&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Bunでは、通常の浮動小数点除算と &lt;code&gt;Math.floor&lt;/code&gt; を &lt;code&gt;Math.imul&lt;/code&gt; とシフトへ置き換える効果が特に大きく出ました。&lt;/p&gt;
&lt;p&gt;CとRustのコンパイラは定数除算をもともと乗算とシフトへ変換できます。それでも、365日項と &lt;code&gt;/4&lt;/code&gt; をまとめ、一つの積を &lt;code&gt;/100&lt;/code&gt; と &lt;code&gt;/400&lt;/code&gt; に共有し、残りの定数を月テーブルへ移した限定範囲版が、この環境では約37〜40%高速でした。&lt;/p&gt;
&lt;p&gt;ただし、これは特定のCPU、コンパイラ、JIT、入力分布に対する結果です。Intel x86-64、別世代のApple Silicon、Node.js/V8、ブラウザ、異なるRust/Clangバージョンで同じ差になるとは限りません。&lt;/p&gt;
&lt;h2&gt;先行研究と今回の位置づけ&lt;/h2&gt;
&lt;p&gt;この実装はHoward Hinnantの &lt;code&gt;days_from_civil&lt;/code&gt; を基礎にしています。また、整数の定数除算を乗算とシフトへ置き換えるstrength reductionは既知の技法です。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;5243&lt;/code&gt; による、0以上9999以下の整数の100除算も既知です。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(value * 5243) &gt;&gt; 19
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;今回の構成は、先の記事「マジックナンバー化による高速な曜日計算」で使用した限定範囲向けの共有積を、&lt;code&gt;days_from_civil&lt;/code&gt; へ展開したものです。&lt;/p&gt;
&lt;p&gt;ポイントは次の組み合わせです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;同じ積 &lt;code&gt;adjustedYear * 5243&lt;/code&gt; から &lt;code&gt;/100&lt;/code&gt; と &lt;code&gt;/400&lt;/code&gt; を取得する&lt;/li&gt;
&lt;li&gt;&lt;code&gt;365 * adjustedYear + floor(adjustedYear / 4)&lt;/code&gt; を &lt;code&gt;floor(1461 * adjustedYear / 4)&lt;/code&gt; にまとめる&lt;/li&gt;
&lt;li&gt;March-basedの月項を、epochオフセットも含む12要素テーブルへ置き換える&lt;/li&gt;
&lt;li&gt;JavaScriptでは &lt;code&gt;Math.imul&lt;/code&gt; と符号なしシフトを明示する&lt;/li&gt;
&lt;li&gt;年を &lt;code&gt;1..9999&lt;/code&gt; に限定し、32-bit範囲内で完結させる&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;NeriとSchneiderはEuclidean affine functionとして、Gregorian calendar変換の乗算・シフト化と依存鎖短縮を体系的に扱っています。したがって、構成要素や一般的な最適化方針を新しいものとして主張するものではありません。&lt;/p&gt;
&lt;p&gt;一方、年1〜9999の &lt;code&gt;days_from_civil&lt;/code&gt; に対して、&lt;code&gt;1461&lt;/code&gt; の年積、&lt;code&gt;/100&lt;/code&gt; と &lt;code&gt;/400&lt;/code&gt; を共有する &lt;code&gt;5243&lt;/code&gt; の積、epoch補正済み月テーブルを組み合わせる同一構成については、調査した公開実装の範囲では確認できませんでした。&lt;/p&gt;
&lt;p&gt;これは、あらゆるCPU、言語、式の形の中で最速または最適であることを意味しません。対象範囲と実行環境を固定した高速化です。&lt;/p&gt;
&lt;h2&gt;逆変換について&lt;/h2&gt;
&lt;p&gt;この記事が扱う &lt;code&gt;days_from_civil&lt;/code&gt; は、年月日を連続した通算日へ変換します。逆向きの変換は通常 &lt;code&gt;civil_from_days&lt;/code&gt; と呼ばれます。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;civil date -- days_from_civil --&gt; serial day
civil date &amp;#x3C;-- civil_from_days --- serial day
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;両者は逆関数ですが、最適化の構造はかなり異なります。&lt;code&gt;civil_from_days&lt;/code&gt; は一つの整数からera、年、月、日を復元する必要があり、使う除算、補正、マジックナンバーも異なります。そのため、別の記事として扱うのが適切です。&lt;/p&gt;
&lt;h2&gt;まとめ&lt;/h2&gt;
&lt;p&gt;年を1〜9999に限定することで、Hinnant型 &lt;code&gt;days_from_civil&lt;/code&gt; の定数除算を、互いに独立した二つの乗算とシフトへ置き換えられました。&lt;/p&gt;
&lt;p&gt;中心となる変換は次の二つです。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;Math.imul(adjustedYear, 1_461) &gt;&gt;&gt; 2;

const centuryProduct = Math.imul(adjustedYear, 5_243);

centuryProduct &gt;&gt;&gt; 19; // adjustedYear / 100
centuryProduct &gt;&gt;&gt; 21; // adjustedYear / 400
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;EPOCH_MONTH_TABLE[month] + day;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apple Silicon上の今回の測定では、通常のHinnant実装より、Bunで約80%、RustとCで約37〜40%実行時間を短縮しました。&lt;/p&gt;
&lt;p&gt;一般的なアプリケーションでは、読みやすく広い年範囲を扱える通常の &lt;code&gt;days_from_civil&lt;/code&gt; で十分です。今回の実装は、年月日変換がホットパスにあり、入力年の範囲が明確で、対象環境で性能差を実測できる場合に向いています。&lt;/p&gt;
&lt;h2&gt;参考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Howard Hinnant, &lt;a href=&quot;https://howardhinnant.github.io/date_algorithms.html&quot;&gt;chrono-Compatible Low-Level Date Algorithms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Cassio Neri and Lorenz Schneider, &lt;a href=&quot;https://arxiv.org/abs/2102.06959&quot;&gt;Euclidean Affine Functions and Applications to Calendar Algorithms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Cassio Neri, &lt;a href=&quot;https://github.com/cassioneri/eaf&quot;&gt;EAF supplementary material&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://veritycost.com/posts/fast-day-of-week-calc-81ee505e/&quot;&gt;マジックナンバー化による高速な曜日計算&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>ふるさと納税で危険な年収帯</title><link>https://veritycost.com/posts/furusato-tax-c0a7d8f0/</link><guid isPermaLink="true">https://veritycost.com/posts/furusato-tax-c0a7d8f0/</guid><description>だいたい◯◯◯万円付近。ふるさと納税の限度額の目安を、公式に基づいて算出すると次のようになる。</description><pubDate>Sun, 05 Apr 2026 08:56:47 GMT</pubDate><content:encoded>&lt;p&gt;ふるさと納税の限度額の目安を、公式に基づいて算出すると次のようになる。&lt;/p&gt;
&lt;figure class=&quot;media-figure&quot; style=&quot;max-width: 1024px; margin: 1rem auto 1.25rem;&quot;&gt;
  ![ふるさと納税の整理図](../../assets/furusato_true.svg)
  &lt;figcaption&gt;なぞのジャンプがある&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;上記の図の通り年収440万前後および650万円前後でジャンプが発生している。
これは主に所得税率の境目で起きる。&lt;/p&gt;
&lt;h2&gt;傾きが急になる理由&lt;/h2&gt;
&lt;p&gt;ふるさと納税の控除は、ざっくり次の3つに分かれる。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;所得税控除&lt;/li&gt;
&lt;li&gt;住民税基本分&lt;/li&gt;
&lt;li&gt;住民税特例分&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;このうち、上限額を実質的に決めているのは&lt;code&gt;住民税特例分&lt;/code&gt;である。&lt;br&gt;
&lt;code&gt;住民税特例分&lt;/code&gt;には住民税所得割額の20%という上限があるため、この枠をどこまで使い切れるかで、ふるさと納税の限度額が決まる。&lt;/p&gt;
&lt;p&gt;追加で寄附した金額は、まず&lt;code&gt;所得税控除&lt;/code&gt;と&lt;code&gt;住民税基本分&lt;/code&gt;に配分され、残りを&lt;code&gt;住民税特例分&lt;/code&gt;が受け持つ。&lt;/p&gt;
&lt;p&gt;ここで、所得税率が切り替わって高くなると、追加の寄附額に対して &lt;code&gt;所得税控除&lt;/code&gt; の割合がそれまでより多くなる。すると、そのぶん &lt;code&gt;住民税特例分&lt;/code&gt; が受け持つ額はそれまでより小さくなる。&lt;/p&gt;
&lt;p&gt;つまり、所得税率が高くなったあとは、同じ1万円を追加で寄附しても &lt;code&gt;住民税特例分の枠&lt;/code&gt; の減り方がそれまでより遅くなる。&lt;br&gt;
その結果、住民税特例分の上限に達するまでに許される寄附額が増え、グラフの傾きが急になる。税率差が大きい方が急になりやすい。&lt;/p&gt;
&lt;p&gt;逆に、税率帯が変わらない区間では、追加寄附1万円あたりで &lt;code&gt;住民税特例分&lt;/code&gt; をどれだけ使うかもほぼ一定なので、グラフの傾きもほぼ一定になる。&lt;/p&gt;
&lt;h2&gt;細かいギザギザ&lt;/h2&gt;
&lt;p&gt;細かいギザギザは社会保険の等級表を表している。曲線は階段的になり、局所的な戻りや細かい折れは等級切替の影響が強い。&lt;/p&gt;
&lt;figure class=&quot;media-figure&quot; style=&quot;max-width: 720px; margin: 1rem auto 1.25rem;&quot;&gt;
  ![ふるさと納税の拡大図](../../assets/furusato_true_zoom_300_400_v4.svg)
  &lt;figcaption&gt;等級表切り替えの影響&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;N字型折れ&lt;/h2&gt;
&lt;figure class=&quot;media-figure&quot; style=&quot;max-width: 720px; margin: 1rem auto 1.25rem;&quot;&gt;
  ![ふるさと納税の600万から700万円帯拡大図](../../assets/furusato_true_zoom_600_700_v4.svg)
  &lt;figcaption&gt;654万円付近のN字型折れ。&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;654万付近のN字型折れは制度上ありうるもの。年収653万は月額だと544,166円、654万だと545,000円。この1万円差で標準報酬月額の等級が切り替わる。その結果、健康保険・支援金・厚生年金の従業員負担が一段上がり、ふるさと納税上限の変化は一時的に鈍化し、場合によってはマイナスになる。450万付近も似ている。&lt;/p&gt;
&lt;p&gt;この議論は最も単純なモデルをベースとしており、扶養控除、保険、住宅ローン、iDeCoなど控除が増えれば増えるほど年収がずれていく。自分の状況に合わせたいなら税理士に確認するのが良いでしょう。&lt;/p&gt;
&lt;p&gt;ふるさと納税の早見表などをもとに寄付する場合、この近辺では見落としが発生することがあり、例えば650万円から逆算するなら限度額超過に注意が必要です。よくわからなかったら保守的に考えるのが楽です。多少限度額を越えても損はしません。&lt;/p&gt;
&lt;h2&gt;細かい補足&lt;/h2&gt;
&lt;p&gt;できるだけ単純なモデルとした&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;対象は &lt;code&gt;単身・扶養なし・iDeCo等なし&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;会社員前提&lt;/li&gt;
&lt;li&gt;協会けんぽ東京&lt;/li&gt;
&lt;li&gt;40歳未満&lt;/li&gt;
&lt;li&gt;賞与なし&lt;/li&gt;
&lt;li&gt;社会保険料は年収固定率ではなく、標準報酬月額の等級表ベースで計算する&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;一般的な図表との違い&lt;/h3&gt;
&lt;p&gt;よく使われる簡便式は住民税特別控除を限界税率1本で使うため、境目で見かけ上の階段大ジャンプが出る。簡便式は、国税庁・総務省の控除ルールから上限額を逆算した「目安用」の式。実際の税務実務そのものが、この簡便式を使って最終税額を確定しているわけではない&lt;/p&gt;
&lt;h3&gt;年収1000万以上&lt;/h3&gt;
&lt;p&gt;所得税率の切り替わりのタイミングで同様の事象が発生する&lt;/p&gt;
&lt;p&gt;なににせよ、ふるさと納税は、共同体に責任を負う市民の立場からすれば、速やかに廃止されるべき制度でしょう。&lt;/p&gt;
&lt;h2&gt;参考コード&lt;/h2&gt;
&lt;p&gt;bun&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;// furusato_tufte.ts
// bun run furusato_tufte.ts
//
// 出力:
//   - furusato_tufte.svg
//
// 前提:
//   - 単身
//   - 配偶者控除・扶養控除なし
//   - iDeCo なし
//   - 基礎控除あり
//   - 会社員（協会けんぽ東京・40歳未満・賞与なし）
//   - 社会保険料は標準報酬月額の等級表で計算
//   - 300万円〜1000万円を 1万円刻み
//
// ねらい:
//   - ジャンプ地点を自動検出
//   - 注釈は白い箱 + 細いリーダー線
//   - 660万円 / 850万円の補助線は出さない
//   - x/y 軸と目盛りを明示

const W = 1400;
const H = 930;

const M = {
  top: 80,
  right: 80,
  bottom: 200,
  left: 110,
};

const DEFAULT_X_MIN = 300; // 万円
const DEFAULT_X_MAX = 1000; // 万円
const STEP_YEN = 10_000;

const BASIC_DEDUCTION = 480_000;
const RESIDENT_BASIC_DEDUCTION = 430_000;
const MONTHS_PER_YEAR = 12;
const RECONSTRUCTION_SURTAX_RATE = 0.021;

const ASSUMPTIONS = {
  unemploymentInsuranceEmployeeRate: 0.005, // 一般の事業 令和8年度
} as const;

const HEALTH_INSURANCE_TABLE = [
  { upperBound: 63_000, employeeHealth: 2_856.5, employeeSupport: 66.7 },
  { upperBound: 73_000, employeeHealth: 3_349.0, employeeSupport: 78.2 },
  { upperBound: 83_000, employeeHealth: 3_841.5, employeeSupport: 89.7 },
  { upperBound: 93_000, employeeHealth: 4_334.0, employeeSupport: 101.2 },
  { upperBound: 101_000, employeeHealth: 4_826.5, employeeSupport: 112.7 },
  { upperBound: 107_000, employeeHealth: 5_122.0, employeeSupport: 119.6 },
  { upperBound: 114_000, employeeHealth: 5_417.5, employeeSupport: 126.5 },
  { upperBound: 122_000, employeeHealth: 5_811.5, employeeSupport: 135.7 },
  { upperBound: 130_000, employeeHealth: 6_205.5, employeeSupport: 144.9 },
  { upperBound: 138_000, employeeHealth: 6_599.5, employeeSupport: 154.1 },
  { upperBound: 146_000, employeeHealth: 6_993.5, employeeSupport: 163.3 },
  { upperBound: 155_000, employeeHealth: 7_387.5, employeeSupport: 172.5 },
  { upperBound: 165_000, employeeHealth: 7_880.0, employeeSupport: 184.0 },
  { upperBound: 175_000, employeeHealth: 8_372.5, employeeSupport: 195.5 },
  { upperBound: 185_000, employeeHealth: 8_865.0, employeeSupport: 207.0 },
  { upperBound: 195_000, employeeHealth: 9_357.5, employeeSupport: 218.5 },
  { upperBound: 210_000, employeeHealth: 9_850.0, employeeSupport: 230.0 },
  { upperBound: 230_000, employeeHealth: 10_835.0, employeeSupport: 253.0 },
  { upperBound: 250_000, employeeHealth: 11_820.0, employeeSupport: 276.0 },
  { upperBound: 270_000, employeeHealth: 12_805.0, employeeSupport: 299.0 },
  { upperBound: 290_000, employeeHealth: 13_790.0, employeeSupport: 322.0 },
  { upperBound: 310_000, employeeHealth: 14_775.0, employeeSupport: 345.0 },
  { upperBound: 330_000, employeeHealth: 15_760.0, employeeSupport: 368.0 },
  { upperBound: 350_000, employeeHealth: 16_745.0, employeeSupport: 391.0 },
  { upperBound: 370_000, employeeHealth: 17_730.0, employeeSupport: 414.0 },
  { upperBound: 395_000, employeeHealth: 18_715.0, employeeSupport: 437.0 },
  { upperBound: 425_000, employeeHealth: 20_192.5, employeeSupport: 471.5 },
  { upperBound: 455_000, employeeHealth: 21_670.0, employeeSupport: 506.0 },
  { upperBound: 485_000, employeeHealth: 23_147.5, employeeSupport: 540.5 },
  { upperBound: 515_000, employeeHealth: 24_625.0, employeeSupport: 575.0 },
  { upperBound: 545_000, employeeHealth: 26_102.5, employeeSupport: 609.5 },
  { upperBound: 575_000, employeeHealth: 27_580.0, employeeSupport: 644.0 },
  { upperBound: 605_000, employeeHealth: 29_057.5, employeeSupport: 678.5 },
  { upperBound: 635_000, employeeHealth: 30_535.0, employeeSupport: 713.0 },
  { upperBound: 665_000, employeeHealth: 32_012.5, employeeSupport: 747.5 },
  { upperBound: 695_000, employeeHealth: 33_490.0, employeeSupport: 782.0 },
  { upperBound: 730_000, employeeHealth: 34_967.5, employeeSupport: 816.5 },
  { upperBound: 770_000, employeeHealth: 36_937.5, employeeSupport: 862.5 },
  { upperBound: 810_000, employeeHealth: 38_907.5, employeeSupport: 908.5 },
  { upperBound: 855_000, employeeHealth: 40_877.5, employeeSupport: 954.5 },
  { upperBound: 905_000, employeeHealth: 43_340.0, employeeSupport: 1_012.0 },
  { upperBound: 955_000, employeeHealth: 45_802.5, employeeSupport: 1_069.5 },
  { upperBound: 1_005_000, employeeHealth: 48_265.0, employeeSupport: 1_127.0 },
  { upperBound: 1_055_000, employeeHealth: 50_727.5, employeeSupport: 1_184.5 },
  { upperBound: 1_115_000, employeeHealth: 53_682.5, employeeSupport: 1_253.5 },
  { upperBound: 1_175_000, employeeHealth: 56_637.5, employeeSupport: 1_322.5 },
  { upperBound: 1_235_000, employeeHealth: 59_592.5, employeeSupport: 1_391.5 },
  { upperBound: 1_295_000, employeeHealth: 62_547.5, employeeSupport: 1_460.5 },
  { upperBound: 1_355_000, employeeHealth: 65_502.5, employeeSupport: 1_529.5 },
  { upperBound: Number.POSITIVE_INFINITY, employeeHealth: 68_457.5, employeeSupport: 1_598.5 },
] as const;

const PENSION_TABLE = [
  { upperBound: 93_000, employeePension: 8_052.0 },
  { upperBound: 101_000, employeePension: 8_967.0 },
  { upperBound: 107_000, employeePension: 9_516.0 },
  { upperBound: 114_000, employeePension: 10_065.0 },
  { upperBound: 122_000, employeePension: 10_797.0 },
  { upperBound: 130_000, employeePension: 11_529.0 },
  { upperBound: 138_000, employeePension: 12_261.0 },
  { upperBound: 146_000, employeePension: 12_993.0 },
  { upperBound: 155_000, employeePension: 13_725.0 },
  { upperBound: 165_000, employeePension: 14_640.0 },
  { upperBound: 175_000, employeePension: 15_555.0 },
  { upperBound: 185_000, employeePension: 16_470.0 },
  { upperBound: 195_000, employeePension: 17_385.0 },
  { upperBound: 210_000, employeePension: 18_300.0 },
  { upperBound: 230_000, employeePension: 20_130.0 },
  { upperBound: 250_000, employeePension: 21_960.0 },
  { upperBound: 270_000, employeePension: 23_790.0 },
  { upperBound: 290_000, employeePension: 25_620.0 },
  { upperBound: 310_000, employeePension: 27_450.0 },
  { upperBound: 330_000, employeePension: 29_280.0 },
  { upperBound: 350_000, employeePension: 31_110.0 },
  { upperBound: 370_000, employeePension: 32_940.0 },
  { upperBound: 395_000, employeePension: 34_770.0 },
  { upperBound: 425_000, employeePension: 37_515.0 },
  { upperBound: 455_000, employeePension: 40_260.0 },
  { upperBound: 485_000, employeePension: 43_005.0 },
  { upperBound: 515_000, employeePension: 45_750.0 },
  { upperBound: 545_000, employeePension: 48_495.0 },
  { upperBound: 575_000, employeePension: 51_240.0 },
  { upperBound: 605_000, employeePension: 53_985.0 },
  { upperBound: 635_000, employeePension: 56_730.0 },
  { upperBound: Number.POSITIVE_INFINITY, employeePension: 59_475.0 },
] as const;

// -----------------------------
// Tax model
// -----------------------------
function salaryDeduction(s: number): number {
  if (s &amp;#x3C;= 1_625_000) return 550_000;
  if (s &amp;#x3C;= 1_800_000) return Math.floor(s * 0.4 - 100_000);
  if (s &amp;#x3C;= 3_600_000) return Math.floor(s * 0.3 + 80_000);
  if (s &amp;#x3C;= 6_600_000) return Math.floor(s * 0.2 + 440_000);
  if (s &amp;#x3C;= 8_500_000) return Math.floor(s * 0.1 + 1_100_000);
  return 1_950_000;
}

function incomeTaxRate(taxable: number): number {
  const rounded = Math.floor(taxable / 1000) * 1000;
  if (rounded &amp;#x3C;= 1_949_000) return 0.05;
  if (rounded &amp;#x3C;= 3_299_000) return 0.1;
  if (rounded &amp;#x3C;= 6_949_000) return 0.2;
  if (rounded &amp;#x3C;= 8_999_000) return 0.23;
  return 0.33;
}

function incomeTaxDeductionAmount(taxable: number): number {
  const rounded = Math.floor(taxable / 1000) * 1000;
  if (rounded &amp;#x3C;= 1_949_000) return 0;
  if (rounded &amp;#x3C;= 3_299_000) return 97_500;
  if (rounded &amp;#x3C;= 6_949_000) return 427_500;
  if (rounded &amp;#x3C;= 8_999_000) return 636_000;
  if (rounded &amp;#x3C;= 17_999_000) return 1_536_000;
  if (rounded &amp;#x3C;= 39_999_000) return 2_796_000;
  return 4_796_000;
}

function lookupByMonthlySalary&amp;#x3C;T extends { upperBound: number }&gt;(
  monthlySalary: number,
  table: readonly T[],
): T {
  return table.find((row) =&gt; monthlySalary &amp;#x3C; row.upperBound) ?? table[table.length - 1];
}

function socialInsurance(s: number): number {
  const monthlySalary = s / MONTHS_PER_YEAR;
  const healthRow = lookupByMonthlySalary(monthlySalary, HEALTH_INSURANCE_TABLE);
  const pensionRow = lookupByMonthlySalary(monthlySalary, PENSION_TABLE);

  const monthlyHealthInsurance = healthRow.employeeHealth + healthRow.employeeSupport;
  const monthlyEmployeesPension = pensionRow.employeePension;
  const annualUnemploymentInsurance = s * ASSUMPTIONS.unemploymentInsuranceEmployeeRate;

  return Math.round(
    monthlyHealthInsurance * MONTHS_PER_YEAR +
      monthlyEmployeesPension * MONTHS_PER_YEAR +
      annualUnemploymentInsurance,
  );
}

function incomeTaxableIncome(s: number): number {
  return Math.max(0, s - salaryDeduction(s) - BASIC_DEDUCTION - socialInsurance(s));
}

function residentTaxableIncome(s: number): number {
  return Math.max(0, s - salaryDeduction(s) - RESIDENT_BASIC_DEDUCTION - socialInsurance(s));
}

function incomeTaxAmount(taxable: number): number {
  const rounded = Math.floor(taxable / 1000) * 1000;
  const baseTax = Math.max(
    0,
    Math.floor(rounded * incomeTaxRate(rounded) - incomeTaxDeductionAmount(rounded)),
  );
  return Math.floor(baseTax * (1 + RECONSTRUCTION_SURTAX_RATE));
}

function furusatoLimitSimple(s: number): number {
  const taxable = incomeTaxableIncome(s);
  const t = incomeTaxRate(taxable);
  const residentIncomeLevy = residentTaxableIncome(s) * 0.1;
  const denom = 0.9 - t;

  if (denom &amp;#x3C;= 0) return 0;
  return Math.floor(2000 + (0.2 * residentIncomeLevy) / denom);
}

function isFullyDeductible(s: number, donationYen: number): boolean {
  const deductionBase = donationYen - 2_000;
  if (deductionBase &amp;#x3C;= 0) return true;

  const incomeTaxBefore = incomeTaxAmount(incomeTaxableIncome(s));
  const incomeTaxAfter = incomeTaxAmount(Math.max(0, incomeTaxableIncome(s) - deductionBase));
  const incomeTaxReduction = incomeTaxBefore - incomeTaxAfter;
  const residentBasicReduction = deductionBase * 0.1;
  const residentSpecialNeeded = deductionBase - incomeTaxReduction - residentBasicReduction;
  const residentSpecialCap = residentTaxableIncome(s) * 0.1 * 0.2;

  return residentSpecialNeeded &amp;#x3C;= residentSpecialCap;
}

function furusatoLimitTrue(s: number): number {
  let lo = 2_000;
  let hi = furusatoLimitSimple(s) + 200_000;
  while (isFullyDeductible(s, hi)) {
    hi *= 2;
  }

  while (hi - lo &gt; 1) {
    const mid = Math.floor((lo + hi) / 2);
    if (isFullyDeductible(s, mid)) {
      lo = mid;
    } else {
      hi = mid;
    }
  }

  return lo;
}

// -----------------------------
// Data
// -----------------------------
type Point = {
  salaryYen: number;
  salaryMan: number;
  limitYen: number;
};

type Gain = {
  startMan: number;
  endMan: number;
  centerMan: number;
  deltaYen: number;
};

// -----------------------------
// Detect jumps
// -----------------------------
type Jump = {
  index: number;
  salaryBeforeYen: number;
  salaryAfterYen: number;
  limitBefore: number;
  limitAfter: number;
  delta: number;
};

function median(arr: number[]): number {
  const xs = [...arr].sort((a, b) =&gt; a - b);
  const n = xs.length;
  if (n % 2 === 1) return xs[(n - 1) / 2];
  return (xs[n / 2 - 1] + xs[n / 2]) / 2;
}

function fmtYen(n: number): string {
  return n.toLocaleString(&quot;ja-JP&quot;);
}

function fmtMan(nYen: number): string {
  return `${(nYen / 10_000).toFixed(1)}万円`;
}

function fmtSalaryMan(nYen: number): string {
  return `${Math.round(nYen / 10_000)}万`;
}

function fmtDonationMan(nYen: number): string {
  return `${(nYen / 10_000).toFixed(1)}万`;
}

function fmtAxisSalaryMan(nMan: number): string {
  return `${nMan}万`;
}

function fmtAxisDonationMan(nYen: number): string {
  if (nYen === 0) return &quot;0&quot;;
  return `${(nYen / 10_000).toFixed(1)}万`;
}

function fmtDeltaManCompact(nYen: number): string {
  return `+${(nYen / 10_000).toFixed(1)}`;
}

function escapeXml(s: string): string {
  return s.replaceAll(&quot;&amp;#x26;&quot;, &quot;&amp;#x26;amp;&quot;).replaceAll(&quot;&amp;#x3C;&quot;, &quot;&amp;#x26;lt;&quot;).replaceAll(&quot;&gt;&quot;, &quot;&amp;#x26;gt;&quot;);
}

function makeJumpCallout(params: {
  x: number;
  y: number;
  boxX: number;
  boxY: number;
  title: string;
  line1: string;
  line2: string;
}) {
  const { x, y, boxX, boxY, title, line1, line2 } = params;

  const boxW = 320;
  const boxH = 112;
  const anchorX = x &amp;#x3C; boxX ? boxX : boxX + boxW;
  const anchorY = boxY + 56;

  return `
    &amp;#x3C;line x1=&quot;${x}&quot; y1=&quot;${y}&quot; x2=&quot;${anchorX}&quot; y2=&quot;${anchorY}&quot;
          stroke=&quot;#666&quot; stroke-width=&quot;1.2&quot; /&gt;

    &amp;#x3C;circle cx=&quot;${x}&quot; cy=&quot;${y}&quot; r=&quot;4.5&quot; fill=&quot;#111&quot; /&gt;

    &amp;#x3C;rect x=&quot;${boxX}&quot; y=&quot;${boxY}&quot; width=&quot;${boxW}&quot; height=&quot;${boxH}&quot;
          fill=&quot;#fffefa&quot; stroke=&quot;#d8d4cc&quot; stroke-width=&quot;1&quot; rx=&quot;2&quot; /&gt;

    &amp;#x3C;text x=&quot;${boxX + 14}&quot; y=&quot;${boxY + 29}&quot; class=&quot;anno strong&quot;&gt;${escapeXml(title)}&amp;#x3C;/text&gt;
    &amp;#x3C;text x=&quot;${boxX + 14}&quot; y=&quot;${boxY + 61}&quot; class=&quot;anno&quot;&gt;${escapeXml(line1)}&amp;#x3C;/text&gt;
    &amp;#x3C;text x=&quot;${boxX + 14}&quot; y=&quot;${boxY + 91}&quot; class=&quot;anno&quot;&gt;${escapeXml(line2)}&amp;#x3C;/text&gt;
  `;
}

function makeSimpleCallout(params: {
  x: number;
  y: number;
  boxX: number;
  boxY: number;
  title: string;
}) {
  const { x, y, boxX, boxY, title } = params;
  const boxW = 280;
  const boxH = 58;
  const anchorX = x &amp;#x3C; boxX ? boxX : boxX + boxW;
  const anchorY = boxY + boxH / 2;

  return `
    &amp;#x3C;line x1=&quot;${x}&quot; y1=&quot;${y}&quot; x2=&quot;${anchorX}&quot; y2=&quot;${anchorY}&quot;
          stroke=&quot;#666&quot; stroke-width=&quot;1.2&quot; /&gt;

    &amp;#x3C;rect x=&quot;${boxX}&quot; y=&quot;${boxY}&quot; width=&quot;${boxW}&quot; height=&quot;${boxH}&quot;
          fill=&quot;#fffefa&quot; stroke=&quot;#d8d4cc&quot; stroke-width=&quot;1&quot; rx=&quot;2&quot; /&gt;

    &amp;#x3C;text x=&quot;${boxX + 14}&quot; y=&quot;${boxY + 37}&quot; class=&quot;anno strong&quot;&gt;${escapeXml(title)}&amp;#x3C;/text&gt;
  `;
}

type AnnotationLayout = {
  x: number;
  y: number;
  boxX: number;
  boxY: number;
};

function overlaps(a: AnnotationLayout, b: AnnotationLayout): boolean {
  const boxW = 320;
  const boxH = 112;
  return !(
    a.boxX + boxW &amp;#x3C;= b.boxX ||
    b.boxX + boxW &amp;#x3C;= a.boxX ||
    a.boxY + boxH &amp;#x3C;= b.boxY ||
    b.boxY + boxH &amp;#x3C;= a.boxY
  );
}

function clamp(n: number, min: number, max: number): number {
  return Math.max(min, Math.min(max, n));
}

const BOX_W = 320;
const BOX_H = 112;
const BOX_GAP_X = 30;
const BOX_GAP_Y = 20;
const CURVE_CLEARANCE = 18;

type ChartConfig = {
  xMin: number;
  xMax: number;
  output: string;
  title: string;
  subtitle: string;
  limitFn: (salaryYen: number) =&gt; number;
  showJumps: boolean;
  simpleCalloutSalaryMans?: number[];
};

type ChartData = {
  points: Point[];
  gains: Gain[];
  diffs: number[];
  jumps: Jump[];
  plotW: number;
  plotH: number;
  maxY: number;
  gainBandTop: number;
  gainBandH: number;
  gainBandBottom: number;
  gainMax: number;
  sx: (xMan: number) =&gt; number;
  sy: (y: number) =&gt; number;
};

function buildChart(config: ChartConfig): ChartData {
  const points: Point[] = [];
  for (let s = config.xMin * 10_000; s &amp;#x3C;= config.xMax * 10_000; s += STEP_YEN) {
    points.push({
      salaryYen: s,
      salaryMan: s / 10_000,
      limitYen: config.limitFn(s),
    });
  }

  const gains: Gain[] = [];
  for (let startMan = config.xMin; startMan &amp;#x3C; config.xMax; startMan += 50) {
    const startYen = startMan * 10_000;
    const endYen = (startMan + 50) * 10_000;
    gains.push({
      startMan,
      endMan: startMan + 50,
      centerMan: startMan + 25,
      deltaYen: config.limitFn(endYen) - config.limitFn(startYen),
    });
  }

  const diffs: number[] = [];
  for (let i = 1; i &amp;#x3C; points.length; i++) {
    diffs.push(points[i].limitYen - points[i - 1].limitYen);
  }

  const baseStep = median(diffs);
  const jumpThreshold = baseStep * 3;
  const jumps: Jump[] = [];
  for (let i = 1; i &amp;#x3C; points.length; i++) {
    const delta = points[i].limitYen - points[i - 1].limitYen;
    if (delta &gt; jumpThreshold) {
      jumps.push({
        index: i,
        salaryBeforeYen: points[i - 1].salaryYen,
        salaryAfterYen: points[i].salaryYen,
        limitBefore: points[i - 1].limitYen,
        limitAfter: points[i].limitYen,
        delta,
      });
    }
  }

  const plotW = W - M.left - M.right;
  const plotH = H - M.top - M.bottom;
  const maxYRaw = Math.max(...points.map((p) =&gt; p.limitYen));
  const yTickStep = 25_000;
  const maxY = Math.ceil((maxYRaw * 1.08) / yTickStep) * yTickStep;
  const gainBandTop = M.top + plotH + 54;
  const gainBandH = 56;
  const gainBandBottom = gainBandTop + gainBandH;
  const gainMax = Math.max(...gains.map((g) =&gt; g.deltaYen));
  const sx = (xMan: number) =&gt;
    M.left + ((xMan - config.xMin) / (config.xMax - config.xMin)) * plotW;
  const sy = (y: number) =&gt; M.top + plotH - (y / maxY) * plotH;

  return {
    points,
    gains,
    diffs,
    jumps,
    plotW,
    plotH,
    maxY,
    gainBandTop,
    gainBandH,
    gainBandBottom,
    gainMax,
    sx,
    sy,
  };
}

function intersectsCurve(
  layout: AnnotationLayout,
  points: Point[],
  sx: (xMan: number) =&gt; number,
  sy: (y: number) =&gt; number,
): boolean {
  const minX = layout.boxX - CURVE_CLEARANCE;
  const maxX = layout.boxX + BOX_W + CURVE_CLEARANCE;
  const minY = layout.boxY - CURVE_CLEARANCE;
  const maxY = layout.boxY + BOX_H + CURVE_CLEARANCE;

  return points.some((p) =&gt; {
    const px = sx(p.salaryMan);
    const py = sy(p.limitYen);
    return px &gt;= minX &amp;#x26;&amp;#x26; px &amp;#x3C;= maxX &amp;#x26;&amp;#x26; py &gt;= minY &amp;#x26;&amp;#x26; py &amp;#x3C;= maxY;
  });
}

function layoutJumpAnnotations(
  jumps: Jump[],
  points: Point[],
  sx: (xMan: number) =&gt; number,
  sy: (y: number) =&gt; number,
  plotW: number,
  plotH: number,
): AnnotationLayout[] {
  const placed: AnnotationLayout[] = [];

  jumps.forEach((j, idx) =&gt; {
    const x = sx(j.salaryAfterYen / 10_000);
    const y = sy(j.limitAfter);

    if (idx === 0) {
      placed.push({
        x,
        y,
        boxX: clamp(x + BOX_GAP_X, M.left + 10, M.left + plotW - BOX_W - 10),
        boxY: clamp(y + BOX_GAP_Y, M.top + 10, M.top + plotH - BOX_H - 10),
      });
      return;
    }

    if (idx === 1) {
      placed.push({
        x,
        y,
        boxX: clamp(x - BOX_W - BOX_GAP_X, M.left + 10, M.left + plotW - BOX_W - 10),
        boxY: clamp(y - BOX_H - 36, M.top + 10, M.top + plotH - BOX_H - 10),
      });
      return;
    }

    const xCandidates =
      idx % 2 === 0
        ? [x + BOX_GAP_X, x + 80, x - BOX_W - BOX_GAP_X]
        : [x - BOX_W - BOX_GAP_X, x - BOX_W - 80, x + BOX_GAP_X];
    const yCandidates = [y - 96, y - 220, y + BOX_GAP_Y, y - 340, y + 140];

    const defaultLayout: AnnotationLayout = {
      x,
      y,
      boxX: clamp(xCandidates[0], M.left + 10, M.left + plotW - BOX_W - 10),
      boxY: clamp(yCandidates[0], M.top + 10, M.top + plotH - BOX_H - 10),
    };
    let chosenLayout = defaultLayout;
    let found = false;

    for (const candidateX of xCandidates) {
      for (const candidateY of yCandidates) {
        const nextLayout = {
          x,
          y,
          boxX: clamp(candidateX, M.left + 10, M.left + plotW - BOX_W - 10),
          boxY: clamp(candidateY, M.top + 10, M.top + plotH - BOX_H - 10),
        };
        if (placed.some((prev) =&gt; overlaps(prev, nextLayout))) continue;
        if (intersectsCurve(nextLayout, points, sx, sy)) continue;
        chosenLayout = nextLayout;
        found = true;
        break;
      }
      if (found) break;
    }

    placed.push(chosenLayout);
  });

  return placed;
}

function renderChart(config: ChartConfig) {
  const data = buildChart(config);
  const yTickStep = 25_000;
  const polyline = data.points
    .map((p) =&gt; `${data.sx(p.salaryMan)},${data.sy(p.limitYen)}`)
    .join(&quot; &quot;);
  const xTicks: string[] = [];
  for (let x = config.xMin; x &amp;#x3C;= config.xMax; x += 50) {
    xTicks.push(`
    &amp;#x3C;line x1=&quot;${data.sx(x)}&quot; y1=&quot;${M.top + data.plotH}&quot; x2=&quot;${data.sx(x)}&quot; y2=&quot;${M.top + data.plotH + 6}&quot;
          stroke=&quot;#444&quot; stroke-width=&quot;1&quot; /&gt;
    &amp;#x3C;text x=&quot;${data.sx(x)}&quot; y=&quot;${M.top + data.plotH + 24}&quot; text-anchor=&quot;middle&quot; class=&quot;tick&quot;&gt;${fmtAxisSalaryMan(x)}&amp;#x3C;/text&gt;
  `);
  }
  const yTicks: string[] = [];
  for (let y = 0; y &amp;#x3C;= data.maxY; y += yTickStep) {
    yTicks.push(`
    &amp;#x3C;line x1=&quot;${M.left - 6}&quot; y1=&quot;${data.sy(y)}&quot; x2=&quot;${M.left}&quot; y2=&quot;${data.sy(y)}&quot;
          stroke=&quot;#444&quot; stroke-width=&quot;1&quot; /&gt;
    &amp;#x3C;text x=&quot;${M.left - 12}&quot; y=&quot;${data.sy(y) + 5}&quot; text-anchor=&quot;end&quot; class=&quot;tick&quot;&gt;${fmtAxisDonationMan(y)}&amp;#x3C;/text&gt;
  `);
  }
  const gainBars = data.gains
    .map((g) =&gt; {
      const x = data.sx(g.centerMan);
      const topY = data.gainBandBottom - (g.deltaYen / data.gainMax) * (data.gainBandH - 18);
      return `
    &amp;#x3C;line x1=&quot;${x}&quot; y1=&quot;${data.gainBandBottom}&quot; x2=&quot;${x}&quot; y2=&quot;${topY}&quot;
          stroke=&quot;#9a9487&quot; stroke-width=&quot;2&quot; /&gt;
    &amp;#x3C;text x=&quot;${x}&quot; y=&quot;${topY - 6}&quot; text-anchor=&quot;middle&quot; class=&quot;mini&quot;&gt;${fmtDeltaManCompact(g.deltaYen)}&amp;#x3C;/text&gt;
    `;
    })
    .join(&quot;\n&quot;);
  const jumpAnnotations: string[] = [];
  if (config.showJumps) {
    const annotationLayouts = layoutJumpAnnotations(
      data.jumps,
      data.points,
      data.sx,
      data.sy,
      data.plotW,
      data.plotH,
    );
    data.jumps.forEach((j, idx) =&gt; {
      const { x, y, boxX, boxY } = annotationLayouts[idx];
      jumpAnnotations.push(
        makeJumpCallout({
          x,
          y,
          boxX,
          boxY,
          title: `${fmtSalaryMan(j.salaryAfterYen)}付近でジャンプ`,
          line1: `${fmtDonationMan(j.limitBefore)} → ${fmtDonationMan(j.limitAfter)}`,
          line2: `+${fmtDonationMan(j.delta)}`,
        }),
      );
    });
  }
  if (config.simpleCalloutSalaryMans?.length) {
    config.simpleCalloutSalaryMans.forEach((salaryMan, idx) =&gt; {
      const point = data.points.find((p) =&gt; p.salaryMan === salaryMan);
      if (!point) return;
      const x = data.sx(point.salaryMan);
      const y = data.sy(point.limitYen);
      const simpleBoxW = 280;
      const simpleBoxH = 58;
      const boxX =
        idx === 0
          ? clamp(x + BOX_GAP_X, M.left + 10, M.left + data.plotW - simpleBoxW - 10)
          : clamp(x - simpleBoxW - BOX_GAP_X, M.left + 10, M.left + data.plotW - simpleBoxW - 10);
      const boxY =
        idx === 0
          ? clamp(y + BOX_GAP_Y, M.top + 10, M.top + data.plotH - simpleBoxH - 10)
          : clamp(y - simpleBoxH - 36, M.top + 10, M.top + data.plotH - simpleBoxH - 10);
      jumpAnnotations.push(
        makeSimpleCallout({
          x,
          y,
          boxX,
          boxY,
          title: `${salaryMan}万付近でジャンプ`,
        }),
      );
    });
  }
  return {
    svg: `&amp;#x3C;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&amp;#x3C;svg width=&quot;${W}&quot; height=&quot;${H}&quot; viewBox=&quot;0 0 ${W} ${H}&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;
  &amp;#x3C;style&gt;
    .title {
      font-family: Georgia, &quot;Times New Roman&quot;, serif;
      font-size: 30px;
      fill: #111;
    }
    .subtitle {
      font-family: Georgia, &quot;Times New Roman&quot;, serif;
      font-size: 16px;
      fill: #555;
    }
    .axislabel {
      font-family: Georgia, &quot;Times New Roman&quot;, serif;
      font-size: 18px;
      fill: #222;
    }
    .tick {
      font-family: Georgia, &quot;Times New Roman&quot;, serif;
      font-size: 15px;
      fill: #444;
    }
    .anno {
      font-family: Georgia, &quot;Times New Roman&quot;, serif;
      font-size: 16px;
      fill: #222;
    }
    .mini {
      font-family: Georgia, &quot;Times New Roman&quot;, serif;
      font-size: 12px;
      fill: #7a7469;
    }
    .miniLabel {
      font-family: Georgia, &quot;Times New Roman&quot;, serif;
      font-size: 13px;
      fill: #7a7469;
    }
    .strong {
      font-weight: 700;
    }
  &amp;#x3C;/style&gt;
  &amp;#x3C;rect width=&quot;100%&quot; height=&quot;100%&quot; fill=&quot;#fbfbf8&quot; /&gt;

  &amp;#x3C;text x=&quot;${W / 2}&quot; y=&quot;42&quot; text-anchor=&quot;middle&quot; class=&quot;title&quot;&gt;
    ふるさと納税の上限額とジャンプ点
  &amp;#x3C;/text&gt;
  &amp;#x3C;text x=&quot;${W / 2}&quot; y=&quot;68&quot; text-anchor=&quot;middle&quot; class=&quot;subtitle&quot;&gt;
    ${escapeXml(config.subtitle)}
  &amp;#x3C;/text&gt;

  &amp;#x3C;line x1=&quot;${M.left}&quot; y1=&quot;${M.top + data.plotH}&quot; x2=&quot;${M.left + data.plotW}&quot; y2=&quot;${M.top + data.plotH}&quot; stroke=&quot;#222&quot; stroke-width=&quot;1.2&quot; /&gt;
  &amp;#x3C;line x1=&quot;${M.left}&quot; y1=&quot;${M.top}&quot; x2=&quot;${M.left}&quot; y2=&quot;${M.top + data.plotH}&quot; stroke=&quot;#222&quot; stroke-width=&quot;1.2&quot; /&gt;

  ${xTicks.join(&quot;\n&quot;)}
  ${yTicks.join(&quot;\n&quot;)}

  &amp;#x3C;polyline fill=&quot;none&quot; stroke=&quot;#1f1f1f&quot; stroke-width=&quot;2.4&quot; points=&quot;${polyline}&quot; /&gt;

  ${jumpAnnotations.join(&quot;\n&quot;)}

  &amp;#x3C;text x=&quot;24&quot; y=&quot;${M.top - 22}&quot; text-anchor=&quot;start&quot; class=&quot;axislabel&quot;&gt;上限寄附額&amp;#x3C;/text&gt;
  &amp;#x3C;text x=&quot;${M.left}&quot; y=&quot;${data.gainBandTop - 12}&quot; text-anchor=&quot;start&quot; class=&quot;miniLabel&quot;&gt;50万増で増える額&amp;#x3C;/text&gt;
  &amp;#x3C;line x1=&quot;${M.left}&quot; y1=&quot;${data.gainBandBottom}&quot; x2=&quot;${M.left + data.plotW}&quot; y2=&quot;${data.gainBandBottom}&quot;
        stroke=&quot;#c8c2b6&quot; stroke-width=&quot;1&quot; /&gt;
  ${gainBars}
&amp;#x3C;/svg&gt;
`,
    diffs: data.diffs,
    jumps: data.jumps,
  };
}

const charts: ChartConfig[] = [
  {
    xMin: DEFAULT_X_MIN,
    xMax: DEFAULT_X_MAX,
    output: &quot;furusato_tufte.svg&quot;,
    title: &quot;300-1000 simple&quot;,
    subtitle:
      &quot;単身・扶養なし・iDeCoなし・会社員(協会けんぽ東京・40歳未満・賞与なし・簡便逆算式) / 1万円刻み&quot;,
    limitFn: furusatoLimitSimple,
    showJumps: true,
  },
  {
    xMin: 1000,
    xMax: 2000,
    output: &quot;furusato_tufte_1000_2000.svg&quot;,
    title: &quot;1000-2000 simple&quot;,
    subtitle:
      &quot;単身・扶養なし・iDeCoなし・会社員(協会けんぽ東京・40歳未満・賞与なし・簡便逆算式) / 1万円刻み&quot;,
    limitFn: furusatoLimitSimple,
    showJumps: true,
  },
  {
    xMin: 0,
    xMax: 2000,
    output: &quot;furusato_true_0_2000.svg&quot;,
    title: &quot;0-2000 true&quot;,
    subtitle:
      &quot;単身・扶養なし・iDeCoなし・会社員(協会けんぽ東京・40歳未満・賞与なし・控除式を数値探索) / 1万円刻み&quot;,
    limitFn: furusatoLimitTrue,
    showJumps: false,
  },
  {
    xMin: DEFAULT_X_MIN,
    xMax: DEFAULT_X_MAX,
    output: &quot;furusato_true.svg&quot;,
    title: &quot;300-1000 true&quot;,
    subtitle:
      &quot;単身・扶養なし・iDeCoなし・会社員(協会けんぽ東京・40歳未満・賞与なし・控除式を数値探索) / 1万円刻み&quot;,
    limitFn: furusatoLimitTrue,
    showJumps: false,
    simpleCalloutSalaryMans: [440, 650],
  },
  {
    xMin: 1000,
    xMax: 2000,
    output: &quot;furusato_true_1000_2000.svg&quot;,
    title: &quot;1000-2000 true&quot;,
    subtitle:
      &quot;単身・扶養なし・iDeCoなし・会社員(協会けんぽ東京・40歳未満・賞与なし・控除式を数値探索) / 1万円刻み&quot;,
    limitFn: furusatoLimitTrue,
    showJumps: false,
  },
];

for (const chart of charts) {
  const rendered = renderChart(chart);
  await Bun.write(chart.output, rendered.svg);
  const baseStep = median(rendered.diffs);
  const jumpThreshold = baseStep * 3;
  console.log(`[${chart.title}] base step (median): ${baseStep} yen`);
  console.log(`[${chart.title}] jump threshold: ${jumpThreshold} yen`);
  console.log(`[${chart.title}] detected jumps:`);
  for (const j of rendered.jumps) {
    console.log(
      `${fmtMan(j.salaryAfterYen)}: ${fmtYen(j.limitBefore)} -&gt; ${fmtYen(j.limitAfter)} (delta ${fmtYen(j.delta)})`,
    );
  }
  console.log(`Generated: ${chart.output}`);
}
&lt;/code&gt;&lt;/pre&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>GitHubで会社用とプライベートアカウントを分けよう(問題ないよ)</title><link>https://veritycost.com/posts/github-work-personal-accounts-ok-b00557c3bd78/</link><guid isPermaLink="true">https://veritycost.com/posts/github-work-personal-accounts-ok-b00557c3bd78/</guid><description>普段使うサービスで、会社用のアカウントとプライベートのアカウントを分けると便利でセキュアですよね？うっかり間違って仕事のデータを大公開してしまうリスクも小さくなります</description><pubDate>Thu, 28 Jan 2021 15:00:00 GMT</pubDate><content:encoded>&lt;p&gt;普段使うサービスで、会社用のアカウントとプライベートのアカウントを分けると便利でセキュアですよね？うっかり間違って仕事のデータを大公開してしまうリスクも小さくなります&lt;/p&gt;
&lt;p&gt;ただ、日本国内では、アカウント分離はNGで、個人ごとに1つのアカウントに集約しないと規約違反であるという言説が広まっているようです。&lt;/p&gt;
&lt;figure class=&quot;media-figure&quot;&gt;
  ![認識共有用に頑張って書いた図。指摘歓迎です](../../assets/github-work-personal-accounts-ok-b00557c3bd78-01.png)
  &lt;figcaption&gt;認識共有用に頑張って書いた図。指摘歓迎です&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;そこでGitHub Supportに事実を確認したところ(英語)、規約違反ということはないという話でした。会社側で有償のオーガニゼーションを契約し、仕事用アカウントを所属させていれば、各自のプライベートアカウントは無料でOKです。&lt;/p&gt;
&lt;p&gt;ロジックとしては、オーガニゼーションに所属させているアカウントは有償コーポレートアカウント(名称仮)なので、個人アカウントに対する無料アカウントの複数所持禁止条項は無関係となります。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;該当条項&lt;/p&gt;
&lt;p&gt;One person or legal entity may maintain no more than one free Account (略).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;サポートからの回答(根拠1)&lt;/h2&gt;
&lt;p&gt;回答のうち重要な部分を引用します。
以下は共有許可をもらっています。(太字は筆者)&lt;/p&gt;
&lt;p&gt;ポイント1&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Yes, that’s right. The 𝗧𝗼𝗦 that we’re discussing here 𝗮𝗽𝗽𝗹𝗶𝗲𝘀 𝗼𝗻𝗹𝘆 𝘁𝗼 𝗽𝗲𝗿𝘀𝗼𝗻𝗮𝗹 𝗮𝗰𝗰𝗼𝘂𝗻𝘁𝘀. As per GitHub’s Terms of Service, a user is only allowed to have a single free, personal account.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;ポイント2&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;So, again, 𝗶𝘁’𝘀 𝗽𝗲𝗿𝗳𝗲𝗰𝘁𝗹𝘆 𝗳𝗶𝗻𝗲 𝘁𝗼 𝗵𝗮𝘃𝗲 𝗮 𝗽𝗲𝗿𝘀𝗼𝗻𝗮𝗹 𝗮𝗰𝗰𝗼𝘂𝗻𝘁 𝗮𝗻𝗱 𝗮 𝘄𝗼𝗿𝗸 𝗮𝗰𝗰𝗼𝘂𝗻𝘁, it’s just that one of them would need to be on the paid plan in order to meet that criteria of our terms!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;傍証(根拠2)&lt;/h2&gt;
&lt;p&gt;英語圏で当件が騒がれていないこと、 多数のサービスで一字一句同じ条項が使われていること。特に英語圏のほうがコンプライアンス周り厳しい人多いので、誰かが解説するか、論議が巻き起こるはずなのに1つも見つからないのが不思議でした。&lt;/p&gt;
&lt;h2&gt;まとめ&lt;/h2&gt;
&lt;p&gt;GitHubだけが特別ということはありません。
素直にアカウントを分けましょう！&lt;/p&gt;
&lt;h2&gt;SaaSのコスト削減したいなら&lt;/h2&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>Google Sheetsでエドワードタフテっぽいグラフを作る</title><link>https://veritycost.com/posts/google-sheets-tufte-graph-9a0f8a1db59d/</link><guid isPermaLink="true">https://veritycost.com/posts/google-sheets-tufte-graph-9a0f8a1db59d/</guid><description>データ可視化のパイオニアである、エドワードタフテ。わかりやすいところなら、キャッチーな脱パワーポイント、6 pagerやWBRといったAmazonの文化にも影響を与えたようです。</description><pubDate>Sat, 15 Mar 2025 03:17:18 GMT</pubDate><content:encoded>&lt;p&gt;データ可視化のパイオニアである、エドワードタフテ。わかりやすいところなら、キャッチーな脱パワーポイント、6 pagerやWBRといったAmazonの文化にも影響を与えたようです。&lt;/p&gt;
&lt;p&gt;その中でもデータインク比という指標が有名で、これを上げたいところ。&lt;/p&gt;
&lt;p&gt;もちろんドローイングツールを使い、あとからこつこつオブジェクトを足し引きして加工してもよいのですが、スプレッドシートの機能を活用したほうが再現性が高く、更新時の工数も減ります&lt;/p&gt;
&lt;p&gt;というわけでGoogle Sheetsでやってみます。&lt;/p&gt;
&lt;h2&gt;最初のグラフ&lt;/h2&gt;
&lt;p&gt;デフォルトのままです&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/google-sheets-tufte-graph-9a0f8a1db59d-1.png&quot; alt=&quot;値はgemeni作の適当です&quot;&gt;&lt;/p&gt;
&lt;h2&gt;データラベルを使う&lt;/h2&gt;
&lt;p&gt;各データポイントの詳細な数字を表示することができます。&lt;/p&gt;
&lt;p&gt;グラフエディタ-&gt; 設定 -&gt; 系列 -&gt; ラベル&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/google-sheets-tufte-graph-9a0f8a1db59d-2.png&quot; alt=&quot;アイルランドにデータラベルを追加&quot;&gt;&lt;/p&gt;
&lt;p&gt;「数値形式」である程度数字を見やすくしておくこと。今回は3桁区切りを追加しました。&lt;/p&gt;
&lt;h2&gt;データラベルで最後の値だけ数字をつける&lt;/h2&gt;
&lt;p&gt;系列のデータポイントが多すぎたり、最後(最新)の値だけ重要なら最後の値だけ数字表示をつけましょう。&lt;/p&gt;
&lt;p&gt;ラベルは系列とは別の範囲を選択可能です。&lt;/p&gt;
&lt;p&gt;最後の行だけ系列と同じデータを入れた列を作り、それをラベルに設定すればOK。&lt;/p&gt;
&lt;p&gt;デフォルトだとラベルの位置は「自動」であり右側にはみ出ます。&lt;/p&gt;
&lt;p&gt;カスタマイズからデータラベルを「左」あたりにしておきましょう。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/google-sheets-tufte-graph-9a0f8a1db59d-3.png&quot; alt=&quot;ラベル専用列を作り、左にした例&quot;&gt;&lt;/p&gt;
&lt;p&gt;同じ方法で最後だけではなく年といった間隔だけを数字表示することができます&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/google-sheets-tufte-graph-9a0f8a1db59d-4.png&quot; alt=&quot;2年ごとにラベルを貼った例。専用列の数字を入れるか入れないかで決まる&quot;&gt;&lt;/p&gt;
&lt;p&gt;凡例ゼロにする場合、数値ではなく文字にして情報を少し追記しておけば、ラベルに表示できます。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/google-sheets-tufte-graph-9a0f8a1db59d-5.png&quot; alt=&quot;カスタム数値形式なら任意の文字列を付与可能。もう少し右にしたかった&quot;&gt;&lt;/p&gt;
&lt;h2&gt;データポイントに形をつける&lt;/h2&gt;
&lt;p&gt;必要そうならポイントのサイズを操作して形をつけましょう。&lt;/p&gt;
&lt;p&gt;データポイントが多いなら形をつけると逆によくわからなくなります&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/google-sheets-tufte-graph-9a0f8a1db59d-6.png&quot; alt=&quot;◯のこと&quot;&gt;&lt;/p&gt;
&lt;h2&gt;グリッドを消す&lt;/h2&gt;
&lt;p&gt;全データポイントに数値を入れているか、データポイントに形があれば縦罫線は不要でしょう&lt;/p&gt;
&lt;p&gt;横罫線も標準だと濃いめなので薄いほうがよいですね&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/google-sheets-tufte-graph-9a0f8a1db59d-7.png&quot; alt=&quot;さようなら縦罫線&quot;&gt;&lt;/p&gt;
&lt;h2&gt;凡例を加工する&lt;/h2&gt;
&lt;p&gt;ものによっては凡例を消しましょう。&lt;/p&gt;
&lt;p&gt;必要なら「位置」を「内側」にしましょう&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/google-sheets-tufte-graph-9a0f8a1db59d-8.png&quot; alt=&quot;右図が「内側」。凡例を内側にするとグラフが大きくできる&quot;&gt;&lt;/p&gt;
&lt;h2&gt;軸の表示を加工する&lt;/h2&gt;
&lt;p&gt;X軸、Y軸のタイトルは見ればわかるなら消しましょう。不要な情報です。&lt;/p&gt;
&lt;p&gt;Y軸の「倍率」や「数値形式」を操作して桁を圧縮します。&lt;/p&gt;
&lt;p&gt;もちろん必要に応じて正規化してください&lt;/p&gt;
&lt;p&gt;X軸の時系列も「数値形式」で見た目を圧縮しましょう。傾けるのも一つの方法です。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/google-sheets-tufte-graph-9a0f8a1db59d-hero.png&quot; alt=&quot;年を削除など&quot;&gt;&lt;/p&gt;
&lt;p&gt;これでだいたい完成です。労力と可視化のバランスが取れたかと思います。&lt;/p&gt;
&lt;p&gt;色や太さは自動のままなのでメッセージに応じて変更しましょう。御存知の通り、色だけで判別させるのは良くない習慣です。&lt;/p&gt;
&lt;h2&gt;時系列の累積グラフ&lt;/h2&gt;
&lt;p&gt;各系列ごとに累積させたいなら残念ながら専用の列を別に作る必要があります。&lt;/p&gt;
&lt;p&gt;ARRAYFORMULA関数あたりならサクッとできます&lt;/p&gt;
&lt;p&gt;(系列同士の足し算は積み上げグラフでOK)&lt;/p&gt;
&lt;h2&gt;その他&lt;/h2&gt;
&lt;p&gt;Y軸0始まりの0を消すことはできないようです。必要なら図形で隠しましょう&lt;/p&gt;
&lt;p&gt;主グリット線だけデータポイントを大きくすることはできなさそう。月次データで年始だけ強調するなど&lt;/p&gt;
&lt;p&gt;重要データポイントへの注釈はグラフ機能だけではできません。矢印などを引いて加工が必要です&lt;/p&gt;
&lt;h2&gt;最後に&lt;/h2&gt;
&lt;p&gt;今はChatGPTなどに聞けばやり方は教えてくれます&lt;/p&gt;
&lt;p&gt;ゴール設定というかグラフの方針が大事ですね&lt;/p&gt;
&lt;p&gt;ほどほどにやりましょう&lt;/p&gt;
&lt;h2&gt;参考資料&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.eugenewei.com/blog/2017/11/13/remove-the-legend&quot;&gt;Remove the legend to become one - Remains of the Day&lt;/a&gt; (&lt;a href=&quot;http://www.eugenewei.com&quot;&gt;www.eugenewei.com&lt;/a&gt;) - When I started my first job at Amazon.com, as the first analyst in the strategic planning department, I inherited the…&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>How to Reduce AWS Lambda Cold Start Times by Shrinking Deployment Package Size</title><link>https://veritycost.com/en/posts/how-to-reduce-aws-lambda-cold-start-times-by-shrinking-deployment-package-size/</link><guid isPermaLink="true">https://veritycost.com/en/posts/how-to-reduce-aws-lambda-cold-start-times-by-shrinking-deployment-package-size/</guid><description>AWS Lambda cold starts are a common pain point for engineers.</description><pubDate>Wed, 23 Jul 2025 15:33:57 GMT</pubDate><content:encoded>&lt;p&gt;AWS Lambda cold starts are a common pain point for engineers.&lt;/p&gt;
&lt;p&gt;This article highlights a practical way to reduce cold start times: keep your deployment package as small as possible.&lt;/p&gt;
&lt;h2&gt;Why Package Size Matters for Cold Starts&lt;/h2&gt;
&lt;p&gt;Whenever an AWS Lambda function experiences a cold start, the code package (sometimes a ZIP archive) is downloaded from S3. The &lt;strong&gt;smaller the package, the faster the initialization&lt;/strong&gt; — that’s basic physics.&lt;/p&gt;
&lt;p&gt;Furthermore, Lambda enforces a strict &lt;a href=&quot;https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html&quot;&gt;250MB unzipped deployment size limit&lt;/a&gt;, and direct uploads are capped at 50MB zipped. Anything larger requires uploading to S3, adding friction to your CI/CD pipeline.&lt;/p&gt;
&lt;p&gt;Besides performance, minimizing package size accelerates deployments, reduces the attack surface, and can even improve your CI/CD feedback loop. This principle also applies to &lt;a href=&quot;https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html&quot;&gt;Lambda Layers&lt;/a&gt; and container-based deployments. Leaner is always better.&lt;/p&gt;
&lt;h2&gt;Step 1: Audit Your Lambda Deployment Package&lt;/h2&gt;
&lt;p&gt;First, &lt;strong&gt;verify what’s inside your ZIP&lt;/strong&gt;. Is your code package larger than you expected?&lt;/p&gt;
&lt;p&gt;You can download the deployed ZIP from the AWS Lambda console and inspect its contents. On macOS, the following command summarizes directory sizes post-extraction:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;du -sh * 2&gt;/dev/null | sort -hr
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 2: Find Your Biggest Functions (with AWS Config Advanced Query)&lt;/h2&gt;
&lt;p&gt;If you want to identify oversized Lambdas across accounts, leverage AWS Config Advanced Query:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT
  accountId,
  resourceId,
  configuration.codeSize,
WHERE
  resourceType = &apos;AWS::Lambda::Function&apos;
  AND configuration.codeSize &gt; 123456789 -- bytes
ORDER BY
  configuration.codeSize desc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://veritycost.com/en/posts/aws-config-advanced-query-sql-like-queries-for-fast-cloud-insights-aa0e2715a615/&quot;&gt;AWS Config Advanced Query: SQL-Like Queries for Fast Cloud Insights&lt;/a&gt; - Instantly analyze AWS resource configurations across accounts without complex scripts.&lt;/p&gt;
&lt;p&gt;You can also use observability tools like Datadog to monitor Lambda function package sizes in bytes, making it easier to spot unusually large deployments across your environment.&lt;/p&gt;
&lt;p&gt;Watch out for functions with &lt;strong&gt;identical sizes down to the byte&lt;/strong&gt;. This can indicate misconfigured deployments or duplicate artifacts.&lt;/p&gt;
&lt;h2&gt;Step 3: Tips for Reducing Deployment Size&lt;/h2&gt;
&lt;p&gt;Let’s break down optimization tips using TypeScript (Node.js) and Python, two of the most popular Lambda languages.&lt;/p&gt;
&lt;h2&gt;Audit Your Development Dependencies&lt;/h2&gt;
&lt;p&gt;Development-only binaries (like mypy) can easily sneak into your production package, sometimes eating up 50MB or more. Even with separate requirements.txt files for dev and prod, transitive dependencies (e.g., via SQLAlchemy) may still bloat your deploy. &lt;strong&gt;Always verify your artifacts using both manual inspection and your package manager’s tree/list commands.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Watch Out for Duplicate or Monolithic Artifacts&lt;/h2&gt;
&lt;p&gt;It’s surprisingly common to see multiple Lambda functions with different names but &lt;strong&gt;identical, oversized packages&lt;/strong&gt; — especially in monorepos. This usually signals a broken build step or poorly configured packaging logic.&lt;/p&gt;
&lt;h2&gt;Dependency Resolution Pitfalls&lt;/h2&gt;
&lt;p&gt;We’ve seen cases where multiple versions of native libraries (like OpenSSL) are bundled, doubling the size. Audit your dependencies carefully.&lt;/p&gt;
&lt;h2&gt;For TypeScript: Apply Frontend Optimization Practices&lt;/h2&gt;
&lt;p&gt;Never deploy raw code. &lt;strong&gt;Always bundle with tools like &lt;a href=&quot;https://esbuild.github.io/&quot;&gt;esbuild&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Backend engineers unfamiliar with frontend practices often overlook this, resulting in massive unoptimized bundles. Use bundle analyzers (e.g., &lt;a href=&quot;https://www.npmjs.com/package/webpack-bundle-analyzer&quot;&gt;webpack-bundle-analyzer&lt;/a&gt;) to visualize and shrink your output.&lt;/p&gt;
&lt;p&gt;Prefer &lt;a href=&quot;https://nodejs.org/api/esm.html&quot;&gt;ES Modules (ESM)&lt;/a&gt; over CommonJS, as ESM tends to produce smaller, more efficient bundles.&lt;/p&gt;
&lt;h3&gt;Find and Remove Unused Code with Knip&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://knip.dev/&quot;&gt;Knip&lt;/a&gt; is a tool that scans your TypeScript (and JavaScript) projects to detect unused files, exports, and dependencies.&lt;/p&gt;
&lt;p&gt;Use it to keep your Lambda artifacts minimal and free from dead code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;npx knip  # npm
pnpx knip # pnpm
bunx knip # bun
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Review the report, and safely remove what you don’t need. This will help shrink your deployment package and reduce the attack surface.&lt;/p&gt;
&lt;h3&gt;Watch the e18e Initiative&lt;/h3&gt;
&lt;p&gt;For cutting-edge improvements in Node.js ecosystem package size and startup performance, keep an eye on the &lt;a href=&quot;https://e18e.dev/&quot;&gt;e18e initiative&lt;/a&gt;. They are working on reducing polyfills, minimizing legacy code, and supporting more efficient modules. While not specifically aimed at serverless, these improvements naturally benefit AWS Lambda and similar environments.&lt;/p&gt;
&lt;h2&gt;For Python: The .py Purge&lt;/h2&gt;
&lt;p&gt;Python loads .pyc bytecode, so you can safely &lt;strong&gt;delete all .py source files&lt;/strong&gt; from your ZIP after compilation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;python -m compileall -b .
find . -name &quot;*.py&quot; -delete
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The tradeoff: tracebacks won’t show the original source. For production workloads (especially third-party modules), this is usually acceptable — proper error handling matters more than debugging tracebacks.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dev.to/aws-builders/reducing-aws-lambda-cold-starts-3eea&quot;&gt;Reducing AWS Lambda Cold Starts&lt;/a&gt; - Introduction AWS Lambda functions with large dependencies can suffer from significant cold starts.&lt;/p&gt;
&lt;h3&gt;Automate This in CDK&lt;/h3&gt;
&lt;p&gt;If you use AWS CDK, hook this cleanup into your deployment pipeline for repeatable results.&lt;/p&gt;
&lt;h3&gt;Node.js 22+: enableCompileCache&lt;/h3&gt;
&lt;p&gt;Starting with Node.js 22, you can use &lt;a href=&quot;https://nodejs.org/api/module.html#moduleenablecompilecachecachedir&quot;&gt;enableCompileCache()&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This leverages V8’s code cache and stores cached bytecode in temporary files.&lt;/p&gt;
&lt;p&gt;While I haven’t benchmarked the impact on Lambda yet, enabling this option could potentially improve performance if your deployment environment supports it.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;AWS Lambda’s abstraction is powerful, but it’s easy to overlook deployment hygiene.&lt;/p&gt;
&lt;p&gt;Trimming your deployment size is one of the simplest, highest-leverage optimizations for cold start latency, security, and operational agility — not just for Lambda, but for all serverless platforms.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Less is more. Audit, optimize, and delete ruthlessly.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;More resources&lt;/h2&gt;
&lt;div class=&quot;link-card-grid&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://speedrun.nobackspacecrew.com/blog/2025/07/21/the-fastest-node-22-lambda-coldstart-configuration.html&quot;&gt;The Fastest Node 22 Lambda Coldstart Configuration - Speedrun&lt;/a&gt; - Beat bundling by 100 ms.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html&quot;&gt;Best practices for working with AWS Lambda functions&lt;/a&gt; - Best practices for using AWS Lambda.&lt;/p&gt;
&lt;/div&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>日本でレイターステージの採用が難しい理由</title><link>https://veritycost.com/posts/late-stage-hiring-japan/</link><guid isPermaLink="true">https://veritycost.com/posts/late-stage-hiring-japan/</guid><description>スタートアップがレイターステージに差し掛かると、資金に余裕が生まれ、プロダクトも安定し、採用を一気に加速したくなるフェーズに入ります。</description><pubDate>Mon, 14 Jul 2025 06:37:14 GMT</pubDate><content:encoded>&lt;p&gt;スタートアップがレイターステージに差し掛かると、資金に余裕が生まれ、プロダクトも安定し、採用を一気に加速したくなるフェーズに入ります。今までの苦労が報われ、知名度も上がり、ポジションも十分に準備できています。&lt;/p&gt;
&lt;p&gt;が、採れない。&lt;/p&gt;
&lt;p&gt;こんなに頑張ってるのに…やることたくさんあるのに…人がたくさん必要なのに…
なぜでしょうか。&lt;/p&gt;
&lt;h2&gt;想起はされても応募しない&lt;/h2&gt;
&lt;p&gt;まずは、自社目線ではなく「求職者の目線」で考えましょう。&lt;/p&gt;
&lt;p&gt;「リスクを取って挑戦したい」「裁量のある環境で働きたい」「カオスを楽しみたい」といったタイプの人は、レイターでは物足りなく、アーリーステージのスタートアップに向かうでしょう。&lt;/p&gt;
&lt;p&gt;一方で、「安定した企業で働きたい」「ブランドのある会社に行きたい」「技術だけに集中したい」そんな人はメガベンチャーに向かいます。&lt;/p&gt;
&lt;p&gt;レイターステージは、このどちらでもない「中途半端な位置」に見られがちです。&lt;/p&gt;
&lt;h2&gt;求める人物像とのミスマッチ&lt;/h2&gt;
&lt;p&gt;レイターになると、いわゆる「スタートアップっぽい人材」ではなく、やや保守的な人の応募が増えてきます。&lt;/p&gt;
&lt;p&gt;これは、これまでのブランディングや露出の成果でもあります。論理的に言えば、広く認知されればされるほど、そういう層にも届くのは自然な流れであり、むしろそのような層に届いていることが成果の証拠です。&lt;/p&gt;
&lt;p&gt;「スタートアップらしさ」に惹かれる人材のパイが限られていることもあり、あえてその外側にいる人を採用し、組織として適応する準備が必要になります。&lt;/p&gt;
&lt;p&gt;もっとも危険なのは、表面上の経歴(lineage)、肩書きや知名度がAクラスでも、実力としてはB〜Cクラスの人材がタイミング的に集まりがちなことで、彼らが中核ポジションに入ると、会社の成長そのものに大きなダメージを与えます。&lt;/p&gt;
&lt;h2&gt;レイターのSaaS全部同じように見える問題&lt;/h2&gt;
&lt;p&gt;アーリーステージでは、創業メンバーの個性や言葉によって差別化が可能です。
面談でも直接その熱量に触れられるため、候補者にとっても「この人と働きたい」「この船に乗りたい」という意思決定がしやすいフェーズです。&lt;/p&gt;
&lt;p&gt;一方、レイターステージになると、会社の見た目は判で押したように同じになります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;「SaaS ARR〇〇億」&lt;/li&gt;
&lt;li&gt;「プロダクトに手応えが出てきたフェーズ」&lt;/li&gt;
&lt;li&gt;「事業成長に伴い人材強化中」&lt;/li&gt;
&lt;li&gt;「スケール期のSaaSで社会にインパクトを」&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;もはや別のレイターステージの会社名と交換しても、おそらく誰も違和感を持たないでしょう。&lt;/p&gt;
&lt;p&gt;洗練されることは悪いことではありません。むしろ組織として成熟してきた証です。しかし同時に、それは「語れる独自性が失われる」リスクでもあります。&lt;/p&gt;
&lt;p&gt;特に採用市場では、候補者は当然のように複数社を比較検討します。&lt;/p&gt;
&lt;p&gt;エージェント・求人票・SNS・口コミ・知人経由など、インプットの経路は限られており、各社が似たタイミングで、似た言葉で、似たメッセージを発信している状態になります。&lt;/p&gt;
&lt;p&gt;こうなると、候補者の目には「カタログスペックで比較するしかない」ように見えてしまいます。&lt;/p&gt;
&lt;p&gt;加えて、この情報の均質化は、候補者側だけに限りません。
実は、採用に関わる側、つまり人事、現場マネージャー、エージェント、面接官も、似たようなインプットに晒されています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;採用のテンプレート的ノウハウ&lt;/li&gt;
&lt;li&gt;採用系note・HRメディアで繰り返される「勝ちパターン」&lt;/li&gt;
&lt;li&gt;候補者でもありうる採用に携わるメンバーの転職体験・他社比較&lt;/li&gt;
&lt;li&gt;競合他社の採用プロセス&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;など、皆が似たような情報ソースを参照し、似たような“正解”を学んでいるのです。&lt;/p&gt;
&lt;p&gt;その結果、「今風の採用をしているつもり」が、気づかぬうちに“他社と同じことを同じように言っている”状態を生み出しています。&lt;/p&gt;
&lt;p&gt;つまり差別化できないのは、候補者の比較眼が厳しいからではなく、採用側の発信自体が“テンプレート化”してしまっていることが原因でもあります。&lt;/p&gt;
&lt;p&gt;発するメッセージがどこかで聞いたような言葉になるのは、むしろ必然です。差別化ができないのではなく、差別化が崩れていく構造そのものが、レイターステージにはあるのです。&lt;/p&gt;
&lt;h2&gt;本当に会社が伸びるのか&lt;/h2&gt;
&lt;p&gt;「IPOで終わり」がまだスタンダードな日本において、レイターステージに入社する意味は、「入ってからさらに伸びていく期待」がなければ薄れてしまいます。&lt;/p&gt;
&lt;p&gt;ワクワクしたくてスタートアップに来たのに、成長の手応えがなく、「もう国内の刈り取りしか残っていない」と感じたら、優秀な人ほど他へ流れてしまうでしょう。&lt;/p&gt;
&lt;p&gt;特に、国内市場に閉じたビジネスモデルの場合、「天井感」が早く訪れるため、海外展開など、もう一段上のスケール戦略が求められます。一方で海外で勝負となるとさらに一段階経営レベルを引き上げる必要があります。&lt;/p&gt;
&lt;h2&gt;内情が想像以上に伝わっている&lt;/h2&gt;
&lt;p&gt;このフェーズでは、退職者も増え、社員数も増え、「社員の知人経由」で社内の雰囲気や実態がかなりの精度で知られるようになります。&lt;/p&gt;
&lt;p&gt;つまり、候補者は応募前に「どんな会社か」だいたい把握していると考えるべきです。&lt;/p&gt;
&lt;p&gt;逆に言えば、内情を知ろうともしない候補者は、ミスマッチの可能性が高いかもしれません。&lt;/p&gt;
&lt;p&gt;自社を、知人に心から推薦できるでしょうか？
競合と比べて魅力があると言えるでしょうか？&lt;/p&gt;
&lt;p&gt;採用をブーストする前に、まずは魅力の根本を見直す必要があるかもしれません。(プロダクト開発と同じ話ですね)&lt;/p&gt;
&lt;p&gt;魅力ある組織にするために採用したいというのは一理ありますが、鶏が先か卵が先か状態です。&lt;/p&gt;
&lt;h2&gt;レイター特有の問題に対処できる人材がまだ少ない&lt;/h2&gt;
&lt;p&gt;特に日本では、SaaSやスタートアップのスケール経験が浅く、「組織フェーズの壁」に直面したときにナレッジも人材も足りない状況です。&lt;/p&gt;
&lt;p&gt;このフェーズを乗り越えるには、スケール経験者が必要ですが、そもそも数がいません。&lt;/p&gt;
&lt;p&gt;これは構造的な問題であり、10〜20年かけて徐々に解消されていくものでしょう。&lt;/p&gt;
&lt;h2&gt;応募してくれたのにアトラクトできない&lt;/h2&gt;
&lt;p&gt;ここまでの壁を突破し、カジュアル面談まで進んだのに断られるのは悲しいことです。全員承諾はもともと無理とはいえ、承諾率が低すぎることはレイターステージ特有のなにかがあるかもしれません。&lt;/p&gt;
&lt;p&gt;思えばアーリーステージでは創業メンバーが熱を持って直接語りかけていました。今は、最近入社したマネージャーが業務に忙殺される中、気もそぞろに話すだけです。ビジョンはもとより、現場で行われている業務の解像度も追いついていないかもしれません。&lt;/p&gt;
&lt;p&gt;「語る人が減っていく」なか、創業の熱と成長の実績を候補者に言葉で伝えられる人を、どう育て、どう配置するかが鍵になります。&lt;/p&gt;
&lt;p&gt;月並みですが、カルチャーデックの活用、面接の設計、トレーニングが必要になります。&lt;/p&gt;
&lt;h2&gt;これらをどうするか&lt;/h2&gt;
&lt;p&gt;もちろん、上記のような構造的問題をすぐに解決するのは難しいですが、「待遇を上げる」ことは最もシンプルで即効性のある手段です。&lt;/p&gt;
&lt;p&gt;「お金ではなく志が大事」という気持ちは大切ですが、それだけで人が集まる時期は、もう過ぎ去っています。&lt;/p&gt;
&lt;p&gt;(2010年代ごろの米国ではIPO後の成長なり買収による早期exitが狙えながら、アーリーより確実性のあるレイターステージはある意味狙い目だったという話もありますね)&lt;/p&gt;
&lt;h2&gt;さいごに&lt;/h2&gt;
&lt;p&gt;ここで述べたことは、採用だけでなく離職予防の観点でも重要です。&lt;/p&gt;
&lt;p&gt;今の採用計画は、現実に即しているでしょうか？&lt;/p&gt;
&lt;p&gt;理想論ではなく、「今の会社がどこに立っているか」を冷静に見極める必要があります。&lt;/p&gt;
&lt;p&gt;※本記事で言う「レイターステージ」は、主に日本市場における文脈を想定しており、おおむねシリーズC後半〜上場直前（Pre-IPO）のフェーズを指します。
アメリカのスタートアップではレイターがもっと長期にわたることもありますが、日本ではこのあたりが「レイター」の実質的な意味合いになります。&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>Optimizing Queries with Functional Indexes: A Practical Guide</title><link>https://veritycost.com/en/posts/optimizing-queries-with-functional-indexes-a-practical-guide-47ed1a45da74/</link><guid isPermaLink="true">https://veritycost.com/en/posts/optimizing-queries-with-functional-indexes-a-practical-guide-47ed1a45da74/</guid><description>Did you know that MySQL 8.0 finally supports functional indexes? We usually think about applying them to JSON columns or for computed…</description><pubDate>Mon, 03 Mar 2025 13:37:09 GMT</pubDate><content:encoded>&lt;p&gt;Did you know that MySQL 8.0 finally supports functional indexes? We usually think about applying them to JSON columns or for computed functions, but in real workloads, these straightforward use cases don’t always pan out. So, here are some other scenarios where functional indexes can actually shine. By the way, the examples here also apply to PostgreSQL, so feel free to take note if that’s your jam.&lt;/p&gt;
&lt;h2&gt;Making Comparison Operators Work with Indexes&lt;/h2&gt;
&lt;p&gt;You’ve probably seen how composite indexes can stop being fully utilized when you introduce a comparison operator somewhere in the middle. For instance, if you have a composite index on &lt;code&gt;(a,b,c)&lt;/code&gt; and then run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;WHERE a = 1 AND b &gt; 1 AND c = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the query will only use the index on &lt;code&gt;(a,b)&lt;/code&gt; effectively — it performs a range scan on b and basically ends there. This is the classic “range scan stops index usage” scenario.&lt;/p&gt;
&lt;p&gt;But if you create a functional index like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE INDEX idx_comp ON TBL (a, (b &gt; 1), c);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can keep using all parts of the index — a, then the boolean result of &lt;code&gt;(b&gt;1)&lt;/code&gt;, then c. Think of &lt;code&gt;(b&gt;1)&lt;/code&gt; as a helper flag to indicate whether b is greater than 1, so you can still do all your filtering via an index.&lt;/p&gt;
&lt;p&gt;However, due to a known MySQL bug, you have to rewrite your query slightly:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;WHERE a = 1
  AND ((b &gt; 1) = TRUE)  -- For now, comparing to TRUE is required to trigger the index
  AND c = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;See the bug report for more details:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://bugs.mysql.com/bug.php?id=99775&quot;&gt;MySQL Bugs: #99775: Functional index and equality condition between two columns&lt;/a&gt; (bugs.mysql.com) - some addl context: a condition is evaluated as IS TRUE; where a=b behaves like a=b IS TRUE.&lt;/p&gt;
&lt;p&gt;Granted, in a simpler scenario like &lt;code&gt;WHERE a=1 AND b&gt;1 AND c=1&lt;/code&gt;, you might just build an index &lt;code&gt;(a,c,b)&lt;/code&gt;. But functional indexes really shine when you need to handle multiple comparisons, like &lt;code&gt;WHERE a &gt; 1 AND b &gt; 1 AND c &gt;1&lt;/code&gt;. Something like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE INDEX idx_comp ON TBL ((a &gt; 1), (b &gt; 1), (c &gt; 1));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;plus a slight tweak to the query can solve otherwise painful multi-range queries.&lt;/p&gt;
&lt;h3&gt;Downsides&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;You’re hardcoding constants into the index. That means if the threshold changes, the index definition also needs to change.&lt;/li&gt;
&lt;li&gt;Logic is now in your index. Whenever that logic changes, you have to remember to update the index, too.&lt;/li&gt;
&lt;li&gt;For now, because of the MySQL bug mentioned, you usually need to modify your query syntax a bit.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Using an Index for NOT Conditions&lt;/h2&gt;
&lt;p&gt;Here’s the deal: negation (NOT) conditions often cause your composite index to behave like it does with simple comparisons, meaning it may drop out after the first column and revert to a range scan. For example, if you have an index on &lt;code&gt;(a,b,c)&lt;/code&gt; and you do:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;WHERE a = 1 AND b &amp;#x3C;&gt; 1 AND c = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The DBMS will likely only make full use of (a) and partially use (b) before giving up. It’s basically the same phenomenon we saw with comparisons like &lt;code&gt;b&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To fix that, you can convert the &lt;code&gt;b&amp;#x3C;&gt;1&lt;/code&gt; logic into a function that returns a boolean-like result. One straightforward technique is to use the absolute value function as a “flag generator.” For instance:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE INDEX idx_comp ON TBL (a, (ABS(b - 1) &gt; 0), c);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By doing &lt;code&gt;ABS(b-1)&lt;/code&gt;, you’re effectively calculating the distance from 1; if that distance is greater than 0, it means b is not equal to 1. Then, in the query, you’d do:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;WHERE a = 1
  AND ((ABS(b - 1) &gt; 0) = TRUE)
  AND c = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That little hack treats &lt;code&gt;(ABS(b-1)&gt;0)&lt;/code&gt; as a stored “flag” in the index, so the optimizer can keep walking through &lt;code&gt;(a,(ABS(b-1)&gt;0),c)&lt;/code&gt; in a single pass.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; If you don’t want to compare against &lt;code&gt;=TRUE&lt;/code&gt;, you could index &lt;code&gt;(ABS(b-1))&lt;/code&gt; itself and then just do &lt;code&gt;WHERE ABS(b-1)&gt;0&lt;/code&gt;. That’ll work too — some people find it a bit cleaner.&lt;/p&gt;
&lt;p&gt;This trick isn’t limited to just a single negation either. If you need multiple negative conditions, or a mix of &lt;code&gt;&gt;&lt;/code&gt; and &lt;code&gt;&amp;#x3C;&gt;&lt;/code&gt;, you can set up your functional index with several boolean-flavored expressions.&lt;/p&gt;
&lt;h3&gt;Downsides&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Same as the comparison operator downsides, plus the rewrite in the query is a bit more confusing.&lt;/li&gt;
&lt;li&gt;You do have to rewrite the query, whereas with a normal index you wouldn’t.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;OR Conditions and Indexing&lt;/h2&gt;
&lt;p&gt;OR conditions are notoriously tough to optimize using a single index. If you do something like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;WHERE price &gt; 100 OR stock &amp;#x3C; 50
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The database usually picks either price or stock to use for an index range scan, not both. If the logic was trivial, you could rewrite this as two separate queries with a UNION, but that’s not always feasible.&lt;/p&gt;
&lt;p&gt;One workaround is to transform OR logic into a function, then build a functional index on that. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE INDEX idx_greatest_price_stock ON products (
    (GREATEST(price - 100, 50 - stock))
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then rewrite your condition to:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;WHERE GREATEST(price - 100, 50 - stock) &gt; 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So if either condition “triggers” &lt;code&gt;(price &gt; 100 or stock &amp;#x3C; 50)&lt;/code&gt;, &lt;code&gt;GREATEST(...)&lt;/code&gt; will be above 0, and the index can handle it.&lt;/p&gt;
&lt;h2&gt;Column IS NOT NULL&lt;/h2&gt;
&lt;p&gt;A check like column &lt;code&gt;IS NOT NULL&lt;/code&gt; might turn into a range scan as well. But if you create:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE INDEX idx_f ON TBL ((column IS NOT NULL));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and then query:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;WHERE (column IS NOT NULL) = TRUE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It can prevent that from degrading into a range scan. Think of it as a “pre-check” for nulls.&lt;/p&gt;
&lt;h2&gt;Flag IS NOT TRUE&lt;/h2&gt;
&lt;p&gt;If you have a three-valued column, say TRUE, FALSE, or NULL, it’s usually best to redesign it to avoid tri-state booleans. But if you’re stuck, you can do:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE INDEX idx_f ON TBL ((flag IS NOT TRUE));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;WHERE (flag IS NOT TRUE) = TRUE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Boom, you now have an index-based approach.&lt;/p&gt;
&lt;h2&gt;Text Matching&lt;/h2&gt;
&lt;p&gt;Comparing long text fields can be expensive, and indexing them can be overkill too. One old-school trick is to store a hash of the URL:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;WHERE url_crc = CRC32(&apos;https://www.example.com/&apos;)
AND url = &apos;https://www.example.com/&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Previously, you had to keep a separate column &lt;code&gt;url_crc&lt;/code&gt;. But now, with functional indexes, you can do:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE INDEX idx_crc ON TBL (CRC32(url));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So you don’t need an extra column if you just want to speed up those lookups (though your query does still have to use &lt;code&gt;CRC32(url)&lt;/code&gt; for the condition). This approach also helps if you’re dealing with huge text-based keys or if, for some reason, you used a VARCHAR-based UUID as a primary key (not recommended!). You can convert it to an integer or shorter hash on the fly and let the index handle the rest.&lt;/p&gt;
&lt;h2&gt;Transforming IN / BETWEEN&lt;/h2&gt;
&lt;p&gt;IN and BETWEEN queries often lead to range scans as well. You can theoretically do something like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE INDEX idx_in ON TBL ((ids IN (0001, 0002)));
CREATE INDEX idx_bet ON TBL ((ids BETWEEN 0001 AND 0002));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is only really viable if you have fixed values or a small range you care about. It’s a pretty niche scenario but can help in edge cases.&lt;/p&gt;
&lt;h2&gt;Converting Any Range Scan Into a Functional Index&lt;/h2&gt;
&lt;p&gt;If you can live with the constant values inside your index definition, you can convert almost any range-based query into something that uses a functional index. For example, even &lt;code&gt;LIKE &apos;STR%&apos;&lt;/code&gt; can be turned into:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE INDEX idx_l1 ON TBL ((str LIKE &apos;STR%&apos;));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;…but obviously, that’s only useful if ‘STR’ is fixed. For partial matches like mid-string or end-of-string, you could do:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE INDEX idx_l2 ON TBL (SUBSTRING(str, 5));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To handle some substring lookups (for example, searching by a domain in email addresses).&lt;/p&gt;
&lt;h2&gt;ORDER BY&lt;/h2&gt;
&lt;p&gt;The same concept applies to ORDER BY. If your WHERE condition becomes a range scan, you might lose index-based sorting. But if you can turn part of the WHERE logic into a function, you can keep all your sorting inside the index. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;WHERE a = 1 AND b &gt; 1
ORDER BY c
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;usually means you lose ordering by c. But with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE INDEX idx_comp ON TBL (a, (b&gt;1), c);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;you can index from a all the way through c. If you need &lt;code&gt;(c,b)&lt;/code&gt; ordering:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE INDEX idx_comp ON TBL (a, (b&gt;1), c, b);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And if you’re sorting on something like &lt;code&gt;(a+b)&lt;/code&gt;, you can also create a functional index on &lt;code&gt;(a+b)&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Other Use Cases&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Date-based filtering, like indexing &lt;code&gt;YEAR()&lt;/code&gt; or &lt;code&gt;DATE()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Price calculations in your queries.&lt;/li&gt;
&lt;li&gt;Case conversions (&lt;code&gt;UPPER()&lt;/code&gt;, &lt;code&gt;LOWER()&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Rounding functions like &lt;code&gt;FLOOR()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Just note that nondeterministic functions (e.g., &lt;code&gt;RAND()&lt;/code&gt;) can’t be used in functional indexes.&lt;/p&gt;
&lt;h2&gt;Comparison of Similar Features&lt;/h2&gt;
&lt;p&gt;Here’s a quick table comparing approaches. Moving from left to right, the column is “closer” to being a real, physical column.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/en/assets/optimizing-queries-with-functional-indexes-a-practical-guide-47ed1a45da74.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Each option shifts where and when you pay the cost. If you only need the computed value for filtering, a functional index is handy. Keep in mind any index will add overhead for updates.&lt;/p&gt;
&lt;p&gt;In other words, functional indexes store the “precomputed” result inside the index itself, letting you filter on it without adding a new column. But there’s always the tradeoff: if this is part of your fundamental logic, maybe consider re-architecting to keep your data model clean.&lt;/p&gt;
&lt;h2&gt;Wrap-Up&lt;/h2&gt;
&lt;p&gt;By encoding logic into functions, you can greatly expand how you leverage indexes. It’s a powerful trick for queries that would otherwise suffer from partial index usage, range scans, or expensive lookups on big text columns. At the same time, be aware of the maintainability tradeoffs — hardcoding constants and rewriting queries can accumulate technical debt if not revisited later.&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>Production Readiness Checklist: Avoid Commonly Overlooked Mistakes</title><link>https://veritycost.com/en/posts/production-readiness-checklist-avoid-commonly-overlooked-mistakes-b2bd35feef1b/</link><guid isPermaLink="true">https://veritycost.com/en/posts/production-readiness-checklist-avoid-commonly-overlooked-mistakes-b2bd35feef1b/</guid><description>Think your team is fully prepared for launch? You might be missing some critical steps.</description><pubDate>Mon, 17 Feb 2025 13:36:27 GMT</pubDate><content:encoded>&lt;p&gt;Think your team is fully prepared for launch? You might be missing some critical steps.&lt;/p&gt;
&lt;p&gt;Most teams have a Production Readiness Checklist, but even well-prepared organizations overlook crucial risks that can cause downtime, rollbacks, or post-launch chaos.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Did you align your release with marketing campaigns to prevent mismatched user expectations?&lt;/li&gt;
&lt;li&gt;Is your rollback plan not just documented, but actually practiced?&lt;/li&gt;
&lt;li&gt;Have you tested synchronization across external services, or just assumed it will work?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Small gaps in preparation can lead to major failures. This checklist highlights the commonly overlooked but high-impact areas to ensure a smooth, failure-resistant launch.&lt;/p&gt;
&lt;p&gt;Let’s dive in. 🚀&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Marketing Readiness: Are You Prepared for Launch-Day Campaigns?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;A product launch isn’t just about shipping code — it’s about delivering a seamless user experience. If marketing and engineering aren’t aligned, users might see ads for features that aren’t live yet, leading to confusion and frustration.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Press releases&lt;/li&gt;
&lt;li&gt;Social media campaigns&lt;/li&gt;
&lt;li&gt;Advertising campaigns&lt;/li&gt;
&lt;li&gt;Paid search ads&lt;/li&gt;
&lt;li&gt;Advance schedule notifications for enterprise customers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;👉 No marketing events planned? Ensure your release strategy accounts for user engagement and visibility.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Release Playbook: Documenting Steps &amp;#x26; Rollback Plans&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;A rollback plan is only as good as its execution. Have you tested it under realistic failure conditions?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Step-by-step release guide is documented&lt;/li&gt;
&lt;li&gt;Rollback plan is in place&lt;/li&gt;
&lt;li&gt;Dry run has been conducted&lt;/li&gt;
&lt;li&gt;Rollback procedure has been practiced&lt;/li&gt;
&lt;li&gt;Clear criteria for halting the release are defined&lt;/li&gt;
&lt;li&gt;Potential fallout is understood in case of failure&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;strong&gt;Quality Assurance: Testing on Rare Devices &amp;#x26; Critical Environments&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;A last-minute bug in an edge-case environment can delay the entire release. Is there a process in place for handling unexpected failures?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Easily test on niche or uncommon but critical devices&lt;/li&gt;
&lt;li&gt;Seamlessly verify functionality in rare yet essential environments&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Have you verified synchronization with external services, systems, or infrastructure?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Not yet&lt;/li&gt;
&lt;li&gt;Not yet, but there’s still buffer time to resolve any potential issues&lt;/li&gt;
&lt;li&gt;Connections successfully tested&lt;/li&gt;
&lt;li&gt;Application executed, and results appear correct&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;strong&gt;Crisis Readiness: War Rooms, Communication, and Contingencies&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Crisis situations require rapid coordination and clear decision-making. Ensure these protocols are in place before launch.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;War room set up with decision-makers on standby — Ensure all key personnel are available and aligned.&lt;/li&gt;
&lt;li&gt;Necessary tools and resources prepared— Incident response tools, monitoring dashboards, and communication platforms should be pre-configured.&lt;/li&gt;
&lt;li&gt;Video calls arranged for remote teams— Ensure seamless communication across locations.&lt;/li&gt;
&lt;li&gt;Video links available for teams on different floors or rooms— Critical for large organizations with distributed teams.&lt;/li&gt;
&lt;li&gt;Snacks and meals organized— A well-fed team performs better in high-pressure situations.&lt;/li&gt;
&lt;li&gt;Rest areas accessible— Burnout during crisis handling is real. Provide spaces for breaks.&lt;/li&gt;
&lt;li&gt;Contingency plan in place if transportation options are no longer available— Consider how late-night deployments impact team logistics.&lt;/li&gt;
&lt;li&gt;System established to capture user feedback — Monitor real-time issues reported by users.&lt;/li&gt;
&lt;li&gt;Handoff plan ready if the crisis extends — Ensure the next shift can seamlessly take over if needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;strong&gt;Rollout Strategy: Gradual Expansion vs. Full-Scale Launch&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Limited rollout — Best for complex changes, high-risk updates, or when monitoring early adopters before full deployment.&lt;/li&gt;
&lt;li&gt;Full-scale release — This option should be avoided whenever possible. If chosen, ensure executive leadership is fully aware of the risks and has explicitly signed off on the potential impact of failure. A failed full-scale release can lead to extended downtime, severe customer impact, and reputational damage.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Regardless of the approach, a solid rollback plan must always be in place.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Final Confidence Check: Are You Truly Ready to Ship?&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Fully prepared and confident for launch — Final review completed, green light given.&lt;/li&gt;
&lt;li&gt;All team concerns have been addressed — Ensure no lingering doubts remain.&lt;/li&gt;
&lt;li&gt;Some team members may still have unspoken concerns if asked privately — Conduct last-minute check-ins with key team members.&lt;/li&gt;
&lt;li&gt;Potential risk areas have been identified&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If any of the above points remain uncertain, take action now before proceeding with the release.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;A successful launch isn’t just the responsibility of one team — it’s the result of seamless cross-functional collaboration. Whether you’re an engineer, product manager, SRE, or marketing lead, your role is crucial in making sure everything aligns before going live.&lt;/p&gt;
&lt;p&gt;This checklist isn’t just a formality; it’s a tool to proactively identify and close gaps before they turn into major failures. Every overlooked detail — whether it’s a missing rollback test, an uncoordinated marketing push, or an unverified external dependency — can impact the entire launch.&lt;/p&gt;
&lt;p&gt;✅ Take ownership beyond your role. If you see a gap, raise it.&lt;/p&gt;
&lt;p&gt;✅ Collaborate cross-functionally. A smooth launch requires alignment across engineering, operations, and business teams.&lt;/p&gt;
&lt;p&gt;✅ Drive the release to success. A launch isn’t done when the code ships — it’s done when customers experience a seamless transition.&lt;/p&gt;
&lt;p&gt;No matter your role, shipping a great product requires an all-hands-on-deck mindset. Let’s make this launch a success — together. 🚀&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>Python Defaults That Hurt Performance</title><link>https://veritycost.com/en/posts/python-pitfall-a2e7585a/</link><guid isPermaLink="true">https://veritycost.com/en/posts/python-pitfall-a2e7585a/</guid><description>Python is often described as slow, but much of that perception comes not from the language itself, but from common usage patterns that quietly hurt performance.</description><pubDate>Fri, 03 Apr 2026 15:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Python is often described as slow.&lt;/p&gt;
&lt;p&gt;In practice, however, much of that perception comes not from the language itself, but from relying on its defaults.&lt;/p&gt;
&lt;p&gt;Python prioritizes readability and backward compatibility. As a result, its default settings are not always optimized for performance.&lt;/p&gt;
&lt;p&gt;In this article, we’ll look at a few common “default traps” that are easy to overlook in production systems.&lt;/p&gt;
&lt;h2&gt;Running an outdated Python version&lt;/h2&gt;
&lt;p&gt;Python versions have a direct impact on performance.&lt;/p&gt;
&lt;p&gt;Starting from Python 3.11, the Faster CPython project has introduced significant interpreter-level optimizations with each release.&lt;/p&gt;
&lt;p&gt;Despite this, older versions are still widely used due to compatibility concerns, operational constraints, or simply because tutorials and onboarding materials lag behind.&lt;/p&gt;
&lt;p&gt;This is one of the simplest—and most impactful—performance traps.&lt;/p&gt;
&lt;p&gt;New versions are typically released around October each year, so it’s worth keeping track.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://devguide.python.org/versions/&quot;&gt;Status of Python versions&lt;/a&gt; (devguide.python.org) - Official page showing the lifecycle and support status of Python versions.&lt;/p&gt;
&lt;h2&gt;Using the standard json library by default&lt;/h2&gt;
&lt;p&gt;Python’s standard library is reliable, but not always fast.&lt;/p&gt;
&lt;p&gt;In many other languages, high-performance JSON handling is closer to the default experience. In Python, this is not necessarily the case.&lt;/p&gt;
&lt;p&gt;Given how central JSON is—across APIs and data processing alike—the standard &lt;code&gt;json&lt;/code&gt; module can become a bottleneck, especially in CPU-bound workloads.&lt;/p&gt;
&lt;p&gt;Libraries such as &lt;code&gt;orjson&lt;/code&gt; or &lt;code&gt;simdjson&lt;/code&gt; are often worth considering.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/ijl/orjson?tab=readme-ov-file#performance&quot;&gt;ijl/orjson: Performance&lt;/a&gt; (github.com) - Performance benchmarks and comparison charts from the orjson README.&lt;/p&gt;
&lt;h2&gt;Recreating HTTP/TCP connections (e.g., boto3)&lt;/h2&gt;
&lt;p&gt;Connection reuse and pooling are widely supported across modern languages and libraries.&lt;/p&gt;
&lt;p&gt;However, how much benefit you get “by default” depends heavily on API design and usage patterns.&lt;/p&gt;
&lt;p&gt;In Python, it is easy to write simple code that unintentionally recreates connections on every request. Without explicit configuration, you may not benefit fully from keep-alive or connection pooling.&lt;/p&gt;
&lt;p&gt;For example, libraries such as boto3 or requests require careful handling of connection reuse.&lt;/p&gt;
&lt;p&gt;If left unaddressed, repeated connection setup can increase both latency and cost.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/programming-with-python.html&quot;&gt;Programming DynamoDB with Python (Boto3)&lt;/a&gt; (docs.aws.amazon.com) - AWS documentation covering best practices such as connection reuse and timeout strategies.&lt;/p&gt;
&lt;h2&gt;Converting iterables to lists too early&lt;/h2&gt;
&lt;p&gt;Python makes heavy use of iterators.&lt;/p&gt;
&lt;p&gt;While lazy evaluation and streaming are widely emphasized across modern languages, it is still common in Python to eagerly convert everything into lists.&lt;/p&gt;
&lt;p&gt;This pattern can lead to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;unnecessary memory allocation&lt;/li&gt;
&lt;li&gt;repeated full scans of data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As data volume grows, this can become a significant performance issue.&lt;/p&gt;
&lt;p&gt;Avoid designs that repeatedly convert iterables to lists, process everything, and then reconstruct iterables again. Pay attention to how many full passes your code performs.&lt;/p&gt;
&lt;h2&gt;Blocking I/O and synchronous execution&lt;/h2&gt;
&lt;p&gt;Python code is often concise, but it can hide significant I/O wait time.&lt;/p&gt;
&lt;p&gt;If you simply write API calls in sequence, they will execute sequentially. In many cases, these operations are also blocking,
meaning no other work is done while waiting for I/O.&lt;/p&gt;
&lt;p&gt;Modern languages often provide stronger default support for asynchronous execution.&lt;/p&gt;
&lt;p&gt;In Python, however, concurrency typically needs to be introduced explicitly.&lt;/p&gt;
&lt;p&gt;When your workload is dominated by I/O latency, using async or parallelism can significantly improve throughput.&lt;/p&gt;
&lt;h2&gt;Tooling also matters (uv, ruff)&lt;/h2&gt;
&lt;p&gt;Python’s tooling ecosystem has long been fragmented, but fast, modern tools such as uv and ruff have gained rapid adoption.&lt;/p&gt;
&lt;p&gt;These tools are designed to be fast even with minimal configuration, and can noticeably improve the developer experience compared to traditional toolchains.&lt;/p&gt;
&lt;p&gt;For new projects, they are worth considering.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.astral.sh/uv/&quot;&gt;uv&lt;/a&gt; (docs.astral.sh) - Official documentation for uv, a fast Python package manager, virtual environment tool, and runner.&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>Pythonのハマりどころ: そのまま使うと遅くなる理由</title><link>https://veritycost.com/posts/python-pitfall-fda494a1/</link><guid isPermaLink="true">https://veritycost.com/posts/python-pitfall-fda494a1/</guid><description>Pythonが遅いと言われる背景には、言語そのものではなく、遅くなりがちな実装パターンがある。よくあるハマりどころを整理する。</description><pubDate>Fri, 03 Apr 2026 15:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Pythonは遅い、とよく言われる。&lt;/p&gt;
&lt;p&gt;しかし実際には、その多くは言語そのものの問題ではなく、「デフォルトのまま使っていること」に起因している。&lt;/p&gt;
&lt;p&gt;Pythonは可読性や後方互換性を重視した設計であり、初期設定は必ずしもパフォーマンス最適ではない。&lt;/p&gt;
&lt;p&gt;本記事では、実運用で見落とされがちな初期設定の罠をいくつか取り上げる。&lt;/p&gt;
&lt;h2&gt;古いバージョンを使い続ける&lt;/h2&gt;
&lt;p&gt;Pythonのバージョンは、そのまま性能に直結する。&lt;/p&gt;
&lt;p&gt;特に3.11以降では、Faster CPythonプロジェクトにより、
バージョンごとにインタプリタレベルでの最適化が大きく進んでいる。&lt;/p&gt;
&lt;p&gt;にもかかわらず、互換性や運用、チュートリアルやオンボーディングの都合で古いバージョンが使われ続けるケースは少なくない。&lt;/p&gt;
&lt;p&gt;これは最も単純で、かつ影響の大きい「罠」である。&lt;/p&gt;
&lt;p&gt;新しいバージョンが毎年10月ごろにリリースされるので確認しよう。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://devguide.python.org/versions/&quot;&gt;Status of Python versions&lt;/a&gt; (devguide.python.org) - Pythonの各バージョンの状態とサポート期間を確認できる公式ページ。&lt;/p&gt;
&lt;h2&gt;標準jsonライブラリをそのまま使う&lt;/h2&gt;
&lt;p&gt;Pythonの標準ライブラリは信頼性が高い一方で、必ずしも高速ではない。他言語では高速なJSON処理が標準で提供されることも多いが、Pythonではそうではない。&lt;/p&gt;
&lt;p&gt;アプリケーションにせよデータ分析にせよほとんどがjsonを操作する時代、特にjsonモジュールが遅く、スピードやCPU負荷に影響しがち。orjsonやsimdjsonといった高速な実装を利用しよう。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/ijl/orjson?tab=readme-ov-file#performance&quot;&gt;ijl/orjson: Performance&lt;/a&gt; (github.com) - orjsonのREADMEにある性能比較とベンチマーク。&lt;/p&gt;
&lt;h2&gt;HTTP/TCP接続を使い捨てる（boto3など）&lt;/h2&gt;
&lt;p&gt;近年の言語やフレームワークでは、接続再利用やコネクションプーリングが標準的に提供されている。ただし、その恩恵をどの程度「デフォルトで受けられるか」は、API設計や利用方法に依存する。&lt;/p&gt;
&lt;p&gt;Pythonでは、シンプルに書いたコードが結果として接続を使い捨てる形になりやすく、明示的に設定しなければ最適な動作にならない場合がある。&lt;/p&gt;
&lt;p&gt;例えば多くのHTTPクライアントは、特別な設定をしない限り、接続の再利用が十分に活用されない。boto3やrequestsなどでは、TCP接続のkeep-aliveやコネクションプーリングの設定が重要になる。&lt;/p&gt;
&lt;p&gt;これらを意識しない場合、不要な接続確立が繰り返され、レイテンシとコストの両方に影響を与える。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/programming-with-python.html&quot;&gt;Programming DynamoDB with Python (Boto3)&lt;/a&gt; (docs.aws.amazon.com) - boto3利用時の設定やコネクション再利用、タイムアウト方針などを説明するAWS公式ドキュメント。&lt;/p&gt;
&lt;h2&gt;iterableをすぐにlistに変換する&lt;/h2&gt;
&lt;p&gt;Pythonでもイテレーターをよく使う。遅延評価やストリーム処理は多くの言語で重視されているが、Pythonではlistへの変換が安易に行われがちである。&lt;/p&gt;
&lt;p&gt;このパターンは、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不要なメモリ確保&lt;/li&gt;
&lt;li&gt;無駄な全件評価&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;を引き起こす。&lt;/p&gt;
&lt;p&gt;特にデータ量が増えたとき、顕著な性能低下につながる。
listに変換して全件処理してはiterableに戻す、といった処理を繰り返す設計は避けよう。何度も何度も全件処理してないか確認すること。&lt;/p&gt;
&lt;h2&gt;ブロッキングI/Oと同期処理に注意&lt;/h2&gt;
&lt;p&gt;Pythonのコードは簡潔に書けるが、その背後でI/O待ちが発生していることは少なくない。&lt;/p&gt;
&lt;p&gt;同期的なAPI呼び出しをそのまま並べると、処理は逐次実行される。
さらに、多くの場合これらはブロッキングI/Oであり、待ち時間のあいだ他の処理を進めることができない。&lt;/p&gt;
&lt;p&gt;モダンな言語では非同期処理が標準的に組み込まれていることが多いが、Pythonでは明示的に設計しなければ逐次実行になる。&lt;/p&gt;
&lt;p&gt;I/O待ちが支配的なワークロードでは、asyncや並列処理を導入することで、スループットが大きく改善する場合がある。&lt;/p&gt;
&lt;h2&gt;uvを使おう&lt;/h2&gt;
&lt;p&gt;Pythonの開発環境は長らく分散していたが、近年はuvやruffといった高速なツールが急速に普及している。&lt;/p&gt;
&lt;p&gt;これらは初期設定のままでも高速に動作するよう設計されており、従来のツールチェーンと比較して開発体験に差が出る。&lt;/p&gt;
&lt;p&gt;新規プロジェクトでは必ず使おう。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.astral.sh/uv/&quot;&gt;uv&lt;/a&gt; (docs.astral.sh) - Pythonのパッケージ管理、仮想環境、実行を高速化するuvの公式ドキュメント。&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>リモートファーストによりフラット化が進み、フルリモートを否定する組織は生きた化石になる</title><link>https://veritycost.com/posts/remote-first-flattening-full-remote-fossil-f3fff80977e3/</link><guid isPermaLink="true">https://veritycost.com/posts/remote-first-flattening-full-remote-fossil-f3fff80977e3/</guid><description>今後の働き方についてフルリモートワークのままにするか、オフィスに戻るか…個人的に悩んでいる方、組織全体で検討している方も多いでしょう。個人レベルの利点なら通勤時間の削減、引っ越しによる豊かな環境、柔軟な仕事の仕方といった面はよく謳われています。組織レベルでも出張代や時間の削減、オ…</description><pubDate>Sat, 20 Nov 2021 06:55:30 GMT</pubDate><content:encoded>&lt;figure class=&quot;media-figure&quot;&gt;
  ![太古の特徴をそのまま残すシーラカンス
  wikipediaより](../../assets/remote-first-flattening-full-remote-fossil-f3fff80977e3-01.png)
  &lt;figcaption&gt;
    [太古の特徴をそのまま残すシーラカンス
    wikipediaより](https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:Latimeria_Chalumnae_-_Coelacanth_-_NHMW.jpg)
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;今後の働き方についてフルリモートワークのままにするか、オフィスに戻るか…個人的に悩んでいる方、組織全体で検討している方も多いでしょう。個人レベルの利点なら通勤時間の削減、引っ越しによる豊かな環境、柔軟な仕事の仕方といった面はよく謳われています。組織レベルでも出張代や時間の削減、オフィス賃料の削減、採用の優位性などは実感できるのではないでしょうか。&lt;/p&gt;
&lt;p&gt;実は、全員フルリモートワークを前提とするリモートファーストには前記以上に組織全体を進化させる大きなメリットがあります。以下、なぜフルリモートワークを組織レベルで推進すべきか説明します。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;フラット化による心理的安全性の担保&lt;/li&gt;
&lt;li&gt;非公式的な経路依存を削減&lt;/li&gt;
&lt;li&gt;組織の成長力アップ&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;＊なんらかの理由で対面業務や物理的作業が発生する職種は除きます。&lt;/p&gt;
&lt;h2&gt;フラット化による心理的安全性の担保&lt;/h2&gt;
&lt;p&gt;はじめに、物理的なオフィスを考えてみましょう。古代より権力者は豪勢な建物で権勢を見せつけてきました。&lt;a href=&quot;https://www.economist.com/business/2021/08/07/chief-executives-are-the-new-monarchs&quot;&gt;現代の大企業幹部も同様です&lt;/a&gt;。メンバーのいる場所より高い階層、豪華な部屋、分厚いドアを好んできましたし、従業員側もそのような場所に入るときに萎縮したり、そもそも近づくことを好まない様子が見受けられます。&lt;/p&gt;
&lt;p&gt;本社勤務とその他の事業所や地域での有利不利、中央と周辺の関係に悩んだことのある方もいらっしゃるでしょう。権力者と文字通り近いことがその人物にもパワーを与え、逆もしかりです。&lt;/p&gt;
&lt;p&gt;組織階層の上位にいる人物は尊重されたい、権力を行使したいと、意識的または無意識的に考え、下位にいるメンバーもそれを敏感に感じ取ることに長けています。&lt;/p&gt;
&lt;p&gt;以下の論考には物理的なオフィスならではの興味深い事例が多数記述されています。
&lt;a href=&quot;https://bunkyo.repo.nii.ac.jp/?action=repository_uri&amp;#x26;item_id=3647&amp;#x26;file_id=37&amp;#x26;file_no=1&quot;&gt;オフィスにおける人間・組織を動かすパワー・ゲーム
―体験・見聞事例からの考察―&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;私が知っている企業幹部は手はじめにブルーの絨毯を自分のオフィス部屋に敷いた。次に、家具のおおいをブルーの布に変え、壁やブラインドまで青く塗り直した。やがて秘書の椅子もブルーになっ た。その幹部のパワーが大きくなるにつれ、ファイル・キャビネットや床、コーヒー茶碗までブルーに変えていった。ほかの幹部はそれほど色に凝っていなかったので、ブルーの威力は驚くほどで、ま もなく彼のシンボルカラーになってしまった。部下たちも周囲に合うからといってブルー系統の服を着るようになり、はじめは冗談のつもりがやがて忠誠を示すシンボルになってしまった。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;他には、業務とは直接関係のない、無意識のイメージによって、特定の人物が有利になったり不利益を被ったりします。アンコンシャス・バイアスと呼ばれ注目されるようになってきました。&lt;/p&gt;
&lt;p&gt;体の大きさ、性別(男性であること)、服装や顔つきといった外見的特性が有利になることを実証した研究も複数あります。この中には声質や声の(物理的な)大きさ、発音やイントネーション、話し方も含まれます。&lt;/p&gt;
&lt;p&gt;今まで述べてきたことが心理的安全性に反すること、心理的安全性が優れた組織には重要な要素であることは繰り返すまでもないでしょう。&lt;/p&gt;
&lt;p&gt;ところが、リモートワークにより物理的に会わなくなったことで、これらは無効化されました。なんとか画面上で威厳や品格を発揮しようとも、大きくないディスプレイのさらに小さなウィンドウ、自宅のすぐ途切れるwifi回線では、スーツもネクタイも識別できません。人間は服装といったちょっとしたシグナルから敏感に権威性を判断するわけですが、それらがほぼ0になります。&lt;/p&gt;
&lt;p&gt;権力を行使する相手がいないこと、権力が行使できないことが、経営層がオフィスに戻りたがり、従業員がそうでもない大きな理由の一つでしょう。&lt;/p&gt;
&lt;p&gt;フルリモート組織でパワーゲームが完全になくなったとは言えませんが、余地が減ったことは間違いありません。&lt;/p&gt;
&lt;p&gt;逆に、従業員側の力は向上しました。長い説教(と称したなにか)をされても、リモートではミュートや音量調整でやり過ごすことができます。録音録画から外部の力を使った反撃も可能でしょう。&lt;/p&gt;
&lt;p&gt;女性なら慣習的に必要とされたメイクの手間と時間が削減され、身体的に移動が難しい人、じっとしていることが難しい人は、特に日常生活に支障を感じていない人と同様に働けるようになりました。マイクもスピーカーも品質がよくないため、声による影響も削減されました。リモートワークでは顔が見えないとよく言われますが、バイアスの点ではそれが逆に&lt;a href=&quot;https://logmi.jp/business/articles/119187&quot;&gt;プラスに&lt;/a&gt;なります。とにかく成果やアウトカムに集中したい人も過ごしやすくなりました。フルリモートワークはD&amp;#x26;Iを支える基盤となります。&lt;/p&gt;
&lt;p&gt;成果に関係のない、パワーゲームに対応している時間が削減されたことも喜ばしいですが、それ以上にバイアスが削減され、評価が公平になる。適材適所で抜擢される。文字通り全員がリモートワークで働くことにより、各自が公平に評価されると同時に公平に人を評価し、心理的安全性が高まり、卓越した組織となるでしょう。&lt;/p&gt;
&lt;p&gt;個々の生産性が向上したこと以上に、組織の向き先が変わることが違いを生みます。目的地に早く到着したいなら、個々の速度の足し算はもとより、皆で協力し目的地の方向へ進路を向け続けることが鍵です。&lt;/p&gt;
&lt;h2&gt;非公式的な情報経路への依存を削減&lt;/h2&gt;
&lt;p&gt;リモートワークで雑談ができなくなったとこぼす人は多く見受けられますね。&lt;/p&gt;
&lt;p&gt;非公式な情報パスは有益ですが、情報の非対称性が大きいのが欠点です。前時的な事例ですがタバコ部屋やゴルフ場、居酒屋でものごとが決まるという事例も古い資料には出てきます。&lt;/p&gt;
&lt;p&gt;ちょっとした雑談による情報収集で優位性が生まれます。雑談は軽く見られがちですが強力で、オフィスなら遠隔地在住の方、家庭の事情がある方、雑談にマイクロアグレッションが含まれそれが不快な方は参加が困難です。情報の流れをコントロールすることでパワーを行使するタイプもいます。&lt;a href=&quot;https://www.mod.go.jp/asdf/meguro/center/20_stdy/arpw04/40numagami01.pdf&quot;&gt;組織デザインではキツネ&lt;/a&gt;と呼ぶようです。&lt;/p&gt;
&lt;p&gt;このようなときはまず無駄をとり、その後冗長性を付け加えることが効果的です。情報共有の時間、コミュニケーションの時間を制度として設けるのが妥当ではないでしょうか。&lt;/p&gt;
&lt;p&gt;加えて、チャットツールやコラボレーションツールといった道具の力を借り、公式な情報経路を最適化しましょう。週次会議で情報共有するとします。3階層あったとして、最上層の会議での報告から考えると、最悪のパターンで全員にその情報が届くには約2週間かかります。 定例会議で情報を降ろしていく流れは、ITがなかった時代のものです。&lt;/p&gt;
&lt;h2&gt;組織の成長力アップ&lt;/h2&gt;
&lt;p&gt;完全フルリモートなら、全国から採用が可能です。採用の対象も広がり、オフィスでは働きづらい方も視野に入ります。採用市場が今後ますます厳しくなるなか、スケールしやすくなりますね。&lt;/p&gt;
&lt;p&gt;顧客も全国がターゲットとなります。ITやデザイン、その他対面が必要のない業種なら、地方から大都市に挑戦することも可能です。市場規模が広がります。&lt;/p&gt;
&lt;p&gt;特筆すべき点として、今まで地域によって細分化された市場がまとまり、プレイヤーが増え競争が盛んになることで、採用にせよ販売にせよ競争力が磨かれます。対してオフィス前提の組織は好意的に見て現状のままでしょう。 東京外の給与水準も上昇余地がありますね。&lt;/p&gt;
&lt;h2&gt;今までフルリモート組織が少なかった理由&lt;/h2&gt;
&lt;p&gt;上記3点のメリットがありながら、今までフルリモート組織は少なかった理由として、誕生から浸透まで時間がかかったことが挙げられます。フルリモート企業を支えるIT基盤となるプロダクトは主に2010年代に生まれているためです。&lt;/p&gt;
&lt;p&gt;ところがコロナ禍で世界中で状況が変わり、ほぼ全員がリモートのなにかしらを体験しました。フルリモートが可能であることを認識(Situational Awareness)し、存在が当たり前になりました。新しい概念を売り込むときに難しい点は認識を変えることです。認識が変わらないと検討さえしてもらえません。しかし、2020年に従業員も幹部も顧客も採用候補者も突然認識が変わり、突然フルリモート組織との付き合いが遡上にのぼるようになりました。今フルリモート組織が受容され始めた背景です。&lt;/p&gt;
&lt;h2&gt;フルリモート組織が効果的な条件&lt;/h2&gt;
&lt;p&gt;新しいものを受け入れるにあたり、行動やプロセスが変わらなければ効果が薄くなります。リモートワークであってもスーツを着ているような習慣が続くようでは成果はおぼつかないでしょう。&lt;/p&gt;
&lt;p&gt;新しいものを受容するときは、新しい慣習を身につけることで初めて大きな差が生まれます。人によってはOSのインストールに例えることもあります。&lt;/p&gt;
&lt;p&gt;テクノロジーの例えになりますが&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;🙈 新しいもの + 古いプロセス e.g. AWSでウォーターフォール&lt;/li&gt;
&lt;li&gt;🙈 古いもの + 新しいプロセス e.g.オンプレミスでアジャイル&lt;/li&gt;
&lt;li&gt;😊 新しいもの+ 新しいプロセス e.g. AWSでアジャイル&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ものごとと慣習、両方を変えることで高い相乗効果を生みます。&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;ハイブリッドワークではだめなのか&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;上記の3点で語られている課題はすべて、オフィスから発生します。慣習的に出社するメンバーが存在する限り、意識的または無意識的に、オフィスにいる従業員やいるタイプの従業員が有利なようにものごとは進むでしょう。&lt;/p&gt;
&lt;p&gt;また慣習については一般に、相反する2つを組み合わせたハイブリッドというのは不可能です。成果を出したいならどちらかしかありえません。&lt;/p&gt;
&lt;p&gt;＊テクノロジー業界の話ですが過去にプライベートクラウドというものがマーケティングメッセージとして生まれ、立ち消えました。&lt;/p&gt;
&lt;h2&gt;まとめ&lt;/h2&gt;
&lt;p&gt;次の10年の壁を乗り越えるにあたり、リモートワークを受容し、新しい慣習を身につけたフルリモート組織が優位に立つでしょう。何より働きやすく、安全で協力的な職場です。&lt;/p&gt;
&lt;p&gt;ぜひみなさんもフルリモート組織への転職を検討したり、組織を生み出したりしてみてください🥳&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>SaaS事業者やCTO/CEO/CFOのためのテクノロジー/セキュリティDD入門…でありDD必要ということだけは覚えておいてください</title><link>https://veritycost.com/posts/saas-technology-security-dd-intro-874d24b74f6c/</link><guid isPermaLink="true">https://veritycost.com/posts/saas-technology-security-dd-intro-874d24b74f6c/</guid><description>SaaS事業者がSaaS等を買収する際、セキュリティやテクノロジーDD(デューデリジェンス: ざっくりいえば買収先に対する状況調査)を怠る事例が多発しています。</description><pubDate>Wed, 03 Jul 2024 08:32:38 GMT</pubDate><content:encoded>&lt;p&gt;SaaS事業者がSaaS等を買収する際、セキュリティやテクノロジーDD(デューデリジェンス: ざっくりいえば買収先に対する状況調査)を怠る事例が多発しています。&lt;/p&gt;
&lt;p&gt;開発面やセキュリティによる事業リスクが見過ごされ、買収してから問題が表面化、買収失敗で特別損失の計上を株主に伝えることになるでしょう。毎回PMI(買収後のプロセス)で担当エンジニアやCTOが悲鳴を上げています。
(買収前にちょっと中身を見ただけでわかるレベルなのに…どうして😭😭)
投資家からの怒号、社内からの悲鳴は避けたいですね。&lt;/p&gt;
&lt;p&gt;もちろん財務や事業、法務、人事、コーポレートITといったバックオフィスといったところはDDで精査しているのですが、なぜか技術面だけぽっかり穴が空いています。&lt;/p&gt;
&lt;p&gt;米国では当然に行われるのになぜ日本では行われないのでしょうか。&lt;/p&gt;
&lt;h2&gt;日本でやらない理由&lt;/h2&gt;
&lt;p&gt;個人的な予測です。&lt;/p&gt;
&lt;h3&gt;(1) 本やweb記事に書いてないから&lt;/h3&gt;
&lt;p&gt;Big 4が出すような資料を定期的に確認してますが、テクノロジーやプロダクトセキュリティDDには言及がありません。各社の専門外というのはありそうですがチェック項目にすらないことがほとんどです。&lt;/p&gt;
&lt;h3&gt;(2) 誰もやっていないから&lt;/h3&gt;
&lt;p&gt;人は真似するもの。M&amp;#x26;A、特にDDは回数も関わる人数も少ないので良い経験も積みにくいでしょう。&lt;/p&gt;
&lt;p&gt;外部のM&amp;#x26;A支援コンサルだと回数積みやすいのですが、結局やってないので0になにを掛けても0です。&lt;/p&gt;
&lt;p&gt;聞き及ぶところでは一部のメガベンチャーではテクノロジーDDを行っているようですが少数派です。&lt;/p&gt;
&lt;h3&gt;(3) 国内SaaS同士の買収の実績がまだ少ないから&lt;/h3&gt;
&lt;p&gt;失敗例や成功例が伝播できてない状態です。&lt;/p&gt;
&lt;h3&gt;(4) IT DDでカバーできそうな気がするから&lt;/h3&gt;
&lt;p&gt;IT DDは多くのM&amp;#x26;Aマニュアルに記載があります。ITとあるのでプロダクトも含まれそうですが実際は社内システムといったコーポレートITだけです。もちろん社内IT統合はコスト効率化の面で重要な観点ですがプロダクトとはまた違います。&lt;/p&gt;
&lt;h3&gt;(5) エンジニアリングが軽視されているから&lt;/h3&gt;
&lt;p&gt;プロダクトを買収する以上、エンジニアリングの重要性は全体の5割とはいいませんが数割を占めるでしょう。単純に全社員における人数でも数割占めることがあります。一方、M&amp;#x26;Aは長期経営計画における重要トピックの一つです。この状態でテクノロジーDDを無視するということは経営陣がエンジニアリングを軽視していると考えざるを得ません。結局全ては言葉ではなく行動です。&lt;/p&gt;
&lt;p&gt;M&amp;#x26;AにCTOが介在しないケースも見聞きします。機密保持のためDDは人数が絞られるとはいえ、さすがに成功させるのは無理があるでしょう。&lt;/p&gt;
&lt;h2&gt;リスク項目とアセスメント&lt;/h2&gt;
&lt;p&gt;どんなところを見ればいいのかの一例です。DDレベルだと大きなインパクトがありそうなところだけになります。&lt;/p&gt;
&lt;h3&gt;開発体制&lt;/h3&gt;
&lt;p&gt;M&amp;#x26;Aは買収先に大きく伸びてもらうのが目的の一つです。&lt;/p&gt;
&lt;p&gt;まずは開発は外注しているのか、開発メンバーのうち社員の割合はどの程度かを確認しましょう。内部統制にも関わる事項です。&lt;/p&gt;
&lt;p&gt;その後買収先のスキルレベルをアセスメントしましょう。技術者外だと判断しづらいですが見る人が見れば一発でランクがわかります。補助的に各種成熟度確認ツールをつかうのもありです。&lt;/p&gt;
&lt;p&gt;買収先が自社より明らかにランクが低い場合、(買収を見送ることをおすすめするものの)人を投入することになるでしょう。&lt;/p&gt;
&lt;p&gt;過去1年程度のインシデント状況とその対応を確認しましょう。インシデントに追われている状態で追加機能開発は困難です。&lt;/p&gt;
&lt;h3&gt;コードベース / アーキテクチャの確認&lt;/h3&gt;
&lt;p&gt;DDは期間も人数も限られており、コードベースの確認に力を入れることはできません。アーキテクチャレベルの確認が主になるでしょう。&lt;/p&gt;
&lt;p&gt;AWSであれば以下の記事も参考にできます。AWSの参照権限をもらったり資料をもらうのはなかなか大変ですが、領収書はPDF一つで明確な事実が確認できます。&lt;/p&gt;
&lt;p&gt;あとはFour Keysといった外部のパフォーマンス基準も使いましょう。その中でもリリース頻度だけ聞けば実力がおおよそわかります。&lt;/p&gt;
&lt;p&gt;大まかな技術的負債とその対応予定も聞きましょう。&lt;/p&gt;
&lt;h3&gt;プロダクトセキュリティ&lt;/h3&gt;
&lt;p&gt;まずは外部脆弱性診断機関からの診断結果を説明してもらいましょう。&lt;/p&gt;
&lt;p&gt;自社でセキュリティエンジニア、特にレッドチーム側の人材を抱えていればスキャンが可能です。法律と常識、モラルの範囲内で確認し、セキュリティリスクが多ければ買収金額の交渉材料となります。&lt;/p&gt;
&lt;p&gt;自社や標準的な開発セキュリティ基準に当てはめて開発状況を確認しましょう。SecurityHubの依頼ぐらいはできるのはないでしょうか&lt;/p&gt;
&lt;p&gt;個人情報や決済関係データの保有状況を忘れずに。&lt;/p&gt;
&lt;p&gt;技術スタックに合わせ、ライセンス周りの遵守状況も確認が必要です。リスク多し。&lt;/p&gt;
&lt;p&gt;セキュリティポリシーや過去のセキュリティインシデント一覧ももらいましょう。&lt;/p&gt;
&lt;h3&gt;テクノロジーコスト&lt;/h3&gt;
&lt;p&gt;プロダクトのユニットエコノミクスはどうか。AWSなら領収書も見ながら削減ポテンシャルを確認しましょう。&lt;/p&gt;
&lt;p&gt;開発系メンバーを自社標準のSaaSに組み込むときに追加費用はどれくらいか。&lt;/p&gt;
&lt;p&gt;契約チェックの一環としてベンダー契約のコミットメント状況や長期契約を合わせて見ます。&lt;/p&gt;
&lt;h2&gt;DDでやること&lt;/h2&gt;
&lt;h3&gt;CTOを巻き込む&lt;/h3&gt;
&lt;p&gt;CTO抜きで話が進み、青天の霹靂となることがあります。プロダクトが重要なら早めに巻き込みましょう。DDの失敗をPMIで巻き返すことはできません。&lt;/p&gt;
&lt;h3&gt;脆弱性診断の結果をもらう&lt;/h3&gt;
&lt;p&gt;ある程度真面目に開発している会社ならば外部脆弱性診断は実施済みのはずです。結果と対応計画を提出してもらいましょう。&lt;/p&gt;
&lt;p&gt;外部脆弱性診断にもレベルがあり、脆弱性診断会社にも能力差があります。診断能力が低いと重要な脆弱性を見過ごします。&lt;/p&gt;
&lt;p&gt;以下のラック社の表でいうLV 2が最低限確認したい診断結果です。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://veritycost.com/assets/saas-technology-security-dd-intro-874d24b74f6c-01.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;自社にセキュリティエンジニアが在籍していれば診断を行った機関のレベルを確認できます。&lt;/p&gt;
&lt;p&gt;深刻な脆弱性が多かったり、修正する能力やキャパシティがないようだったりするようなら買収見送りも検討しましょう。&lt;/p&gt;
&lt;p&gt;もし直近の診断結果が存在しないようなら計画を立ててもらいましょう。(診断依頼の費用負担は売主側だと思います。)&lt;/p&gt;
&lt;p&gt;そもそも診断がない場合はその時点で能力が怪しく、PMIにコストがかかることを想定しましょう。&lt;/p&gt;
&lt;h3&gt;公開リソースを確認する&lt;/h3&gt;
&lt;p&gt;公式サイトや採用サイト/求人広告からおおよそのアーキテクチャを推測できます。&lt;/p&gt;
&lt;p&gt;開発ブログ含めた採用露出を確認しましょう。技術内容のほか、採用力がわかります。&lt;/p&gt;
&lt;p&gt;プロダクトがさわれるならChrome DevToolsやスキャンツールでアーキテクチャや利用技術、バージョンまである程度特定できます。&lt;/p&gt;
&lt;h3&gt;AWS等の領収書をもらう&lt;/h3&gt;
&lt;p&gt;前述のとおりです。&lt;/p&gt;
&lt;p&gt;追加情報として、代理店が介在していればAWS Organization統合の難易度が上がります&lt;/p&gt;
&lt;h3&gt;開発ドキュメントをもらう&lt;/h3&gt;
&lt;p&gt;アーキテクチャや開発プロセスを確認します。&lt;/p&gt;
&lt;p&gt;まずドキュメントがあるか確認しましょう。すぐに出せる資料がないなら危険信号です。&lt;/p&gt;
&lt;h3&gt;体制イメージとロードマップを検討する&lt;/h3&gt;
&lt;p&gt;今後体制をどうしていくのか。特に自社とのギャップがある場合どう引き上げるのか。日本では解雇が難しいため例えば自社では採用しないレベルのメンバーについてはどう育成したり別のポジションを用意したりするのか。&lt;/p&gt;
&lt;p&gt;開発体制や技術標準は自社と揃えるのか、ばらばらにするのか。統制を考えればなるべく高い方に揃えたいですね。&lt;/p&gt;
&lt;p&gt;子会社は採用しづらいことがあります。母集団形成は自社と同じように行うのか、別々なのか。そもそも自社の採用に困っている状況で子会社の支援ができるのか。&lt;/p&gt;
&lt;p&gt;誰が子会社との間に立つのか。100%稼働が取れる人を割り当てるのが望ましいです。&lt;/p&gt;
&lt;p&gt;支援する場合何人ぐらい必要なのか。自社からどれくらい送るのか。送ることができる状況なのか。&lt;/p&gt;
&lt;p&gt;いくつかPMIにまたがって行う項目も記載しましたが、実行計画がイメージできないなら買収を控えたほうが良いでしょう。&lt;/p&gt;
&lt;h3&gt;特にプロダクトセキュリティ関係の実行計画を立てる&lt;/h3&gt;
&lt;p&gt;例えばアプリがどうしようもないならWAFでしのぐしかありません。脆弱性対応で開発がストップも考えられます。&lt;/p&gt;
&lt;p&gt;AWS APIレイヤーで検知や統制するならSecurity Hubなど含め追加コストがかかります。&lt;/p&gt;
&lt;p&gt;なににせよコストがかかるので予算を抑えておく必要があります。&lt;/p&gt;
&lt;h3&gt;自社システムとのデータ連携、API連携方法を検討する&lt;/h3&gt;
&lt;p&gt;連携しないなら買収する意味はないでしょう。&lt;/p&gt;
&lt;h3&gt;新規開発予定を確認する&lt;/h3&gt;
&lt;p&gt;自社ロードマップとのすり合わせが必要です。&lt;/p&gt;
&lt;h3&gt;その他確認事項&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;過去のインシデント事例一覧をもらう。セキュリティも含む&lt;/li&gt;
&lt;li&gt;セキュリティポリシーの有無を確認する&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;見えてきたリスクをもとに、法務等担当者に相談しましょう。買収金額や契約条件の交渉に反映させる必要があります。今後かかるコストも想定しておきましょう。&lt;/p&gt;
&lt;h2&gt;PMIからどう巻き返すか&lt;/h2&gt;
&lt;p&gt;DDの時点でなにもせず買収してしまった時点で内容は運を天に任せるしかありません。DDの内容をカバーしつつ、より具体性を伴わせる必要があります。撤退戦が一番難しく、負け戦は負け方が大事です。&lt;/p&gt;
&lt;h3&gt;一にも二にもセキュリティ&lt;/h3&gt;
&lt;p&gt;とにかくプロダクトセキュリティを優先してアセスメントおよび改善していきましょう。その中でも特にデータ侵害の有無が最優先です。データのありかを深堀りしましょう。&lt;/p&gt;
&lt;p&gt;アプリよりはAWSといったインフラのほうが手を打ちやすいです。どうしようもないアプリに対してすぐできることは各種バージョンアップとWAFぐらい。余裕があればアプリケーションへのランタイムセキュリティ、コードベースへのセキュアコーディング支援ツール、SecurityHub含めたCSPMツールといった形で手間をかけずに自動化したいところです。&lt;/p&gt;
&lt;p&gt;表明保証の期間中にはせめてアセスメントは終わらせたいです。&lt;/p&gt;
&lt;h3&gt;開発改善はきっぱり諦める&lt;/h3&gt;
&lt;p&gt;開発はそのままにしておきます。開発プロセスや体制改善について、セキュリティ関係以外は後回しにせざるを得ません。プロダクトセキュリティに関連するので開発周りのアセスメントだけは早めに行うのが望ましいです。&lt;/p&gt;
&lt;h2&gt;最後に&lt;/h2&gt;
&lt;p&gt;CEO / CFOの方は最悪DDにはセキュリティとテクノロジーDDも必要だということだけを覚えて帰ってください。&lt;/p&gt;
&lt;p&gt;CTOの方は経営戦略上M&amp;#x26;Aは最重要項目の一つであり、それゆえ他人事ではなくCTOが積極的に関わる必要があることを抑えておきましょう。&lt;/p&gt;
&lt;p&gt;無意味無計画な買収が減り、誰もよろこばないPMIが少しでも減ることを願ってやみません。&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>短期海外旅行で便利なもの2023</title><link>https://veritycost.com/posts/short-overseas-trip-useful-items-2023-0864013c4d95/</link><guid isPermaLink="true">https://veritycost.com/posts/short-overseas-trip-useful-items-2023-0864013c4d95/</guid><description>以前はモバイルルーターを使っていた人も、eSIMがおすすめです。短期でその国の通信会社と契約するイメージ。</description><pubDate>Sat, 16 Dec 2023 12:01:00 GMT</pubDate><content:encoded>&lt;h2&gt;eSIM&lt;/h2&gt;
&lt;p&gt;以前はモバイルルーターを使っていた人も、eSIMがおすすめです。&lt;/p&gt;
&lt;p&gt;短期でその国の通信会社と契約するイメージ。しかもWebだけで完結し、何も持ち運ばなくてよいです。そして安い。
国が違ってもほぼいつものスマホと同じ体験に。
よほど古いスマートフォン以外では使えます。&lt;/p&gt;
&lt;p&gt;出発の数営業日前に買っておくのが無難。
やろうと思えば現地に着いた後でも日本のAmazonなりで購入して対応可能です。(電子データのやり取りが主で、送付物はQRコードが印刷された紙だけです)&lt;/p&gt;
&lt;p&gt;設定方法は割愛しますが簡単です。
設定自体にネット接続が必要なので空港のWifiを使いましょう。&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;クレジットカードのコンタクトレス決済 + Apple Pay / Google Pay&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;現金はなるべく減らしたいものの、クレジットカードを見せるのは心配。そんなときはApple Pay等で完結させるのが楽です。&lt;/p&gt;
&lt;p&gt;支払いをクレジットカードでお願いする -&gt; お店の決済端末にスマホをかざす&lt;/p&gt;
&lt;p&gt;で終了です。新しめのお店だったら確実でしょう。
日本と全く同じように使えます。&lt;/p&gt;
&lt;p&gt;ちなみに持っている物理カードがタッチ対応していなくても、Apple Pay等に登録するとスマートフォンでは使えることがあります。電子的なやりとりだからでしょう。&lt;/p&gt;
&lt;figure class=&quot;media-figure&quot;&gt;
  ![お店にこういうマークがあれば確実](../../assets/short-overseas-trip-useful-items-2023-0864013c4d95-01.jpg)
  &lt;figcaption&gt;お店にこういうマークがあれば確実&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;クレジットカードの海外キャッシング&lt;/h2&gt;
&lt;p&gt;現地の現金を準備したいときはクレジットカードの海外キャッシングが便利かつレートが良い傾向にあります。現地で銀行っぽいところのATMにクレカを入れて引き出しましょう。&lt;/p&gt;
&lt;h2&gt;帰国時 税関 Visit Japan Web&lt;/h2&gt;
&lt;p&gt;帰国時の税関処理がWebで完結します。&lt;/p&gt;
&lt;p&gt;名前と裏腹に日本在住者も使えます。使い勝手は微妙…ですが紙よりは良いでしょう。旅行前におおよそ仕上げることもできます&lt;/p&gt;
&lt;h2&gt;オンラインチェックイン&lt;/h2&gt;
&lt;p&gt;意外と多くの方がオンラインチェックインを使っていないように見受けられます。航空会社のチェックインをWebで行えば紙チケットの代わりにQRコードがもらえ、スマホだけで搭乗まで完結できます。
(紙も持っていたほうがリスク分散にはなるという考えもありといえばあり)&lt;/p&gt;
&lt;p&gt;Webのほうが早く席を指定できるので有利です。ツアーでも問題なくオンラインチェックイン可能です。&lt;/p&gt;
&lt;h2&gt;各国の入国電子申請&lt;/h2&gt;
&lt;p&gt;Web申請が必須の国もあれば、紙でOKの国もあります。
とはいえWeb申請が楽なので事前に調べて済ませておきましょう。&lt;/p&gt;
&lt;p&gt;前述のように、入国帰国手続きはペーパーレスでスマホだけで済ませることが可能です。現金を使わないならスマホ一つ(とパスポート)だけで旅行できるかもしれません。&lt;/p&gt;
&lt;h2&gt;カメラで翻訳系のアプリ&lt;/h2&gt;
&lt;p&gt;Googleなどのアプリ。街中で気になったものがあれば内容を確認できます&lt;/p&gt;
&lt;h2&gt;おまけ&lt;/h2&gt;
&lt;h3&gt;洗濯&lt;/h3&gt;
&lt;p&gt;コインランドリーを使えば荷物が圧縮できます。ホテルや周辺にあるか確認しましょう。洗剤の有無も忘れずに。もちろん国内旅行でもできます。&lt;/p&gt;
&lt;h3&gt;ホテル荷物預け入れ&lt;/h3&gt;
&lt;p&gt;ホテルではチェックイン前、チェックアウト後でも荷物を預かってもらえます。立地が良いところだと便利。もちろん国内旅行でもできます。&lt;/p&gt;
&lt;h3&gt;ホテルで朝食付きにするか&lt;/h3&gt;
&lt;p&gt;数日同じだと飽きるので長く泊まるときは素泊まりがおすすめです。
多くのホテルの朝食は個別で頼むことも可能です。もちろん国内旅行でもできます。&lt;/p&gt;
&lt;h3&gt;Uber Eats&lt;/h3&gt;
&lt;p&gt;日本のアカウントでそのまま注文できます。ホテルならロビーかホテル入り口付近で受け取ればよいでしょう。&lt;/p&gt;
&lt;p&gt;ログイン時のSMS認証周りは説明が難しいので割愛。検索してください&lt;/p&gt;
&lt;h3&gt;ホテルと航空券は直接予約&lt;/h3&gt;
&lt;p&gt;直接予約のほうが安いです。&lt;/p&gt;
&lt;p&gt;OTA(Online Travel Agent)は結局マージンを取るので今の時代あまりメリットがありません。キャンペーンで大幅割引のときだけは検討&lt;/p&gt;
&lt;p&gt;特に海外OTAのほとんどは日本の旅行業登録をしておらず、なにかあったときの補償も期待できません。&lt;/p&gt;
&lt;p&gt;上記はすべて国内旅行でも同様です&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>Table-Driven Programming: An Underrated Superpower</title><link>https://veritycost.com/en/posts/table-driven-programming-an-underrated-superpower-cfacc8d4ae17/</link><guid isPermaLink="true">https://veritycost.com/en/posts/table-driven-programming-an-underrated-superpower-cfacc8d4ae17/</guid><description>When you&apos;re cranking out business logic, nothing beats a good lookup table. It&apos;s simple, legible, and keeps the &quot;ifs&quot; from exploding all...</description><pubDate>Sun, 06 Jul 2025 15:21:02 GMT</pubDate><content:encoded>&lt;p&gt;When you’re cranking out business logic, nothing beats a good lookup table. It’s simple, legible, and keeps the “ifs” from exploding all over your codebase.&lt;/p&gt;
&lt;p&gt;From Spaghetti to Spreadsheet&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def get_unit_price(seats):
    if seats &gt;= 100:
        return 12.0
    elif seats &gt;= 50:
        return 14.0
    elif seats &gt;= 25:
        return 16.0
    elif seats &gt;= 10:
        return 18.0
    else:
        return 20.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Swap in a table and watch the clutter disappear:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;pricing_table = [
    {&quot;min&quot;:    0, &quot;rate&quot;: 20.0},   # 0-9 seats -&gt; $20/seat
    {&quot;min&quot;:   10, &quot;rate&quot;: 18.0},   # 10-24 seats
    {&quot;min&quot;:   25, &quot;rate&quot;: 16.0},   # 25-49 seats
    {&quot;min&quot;:   50, &quot;rate&quot;: 14.0},   # 50-99 seats
    {&quot;min&quot;:  100, &quot;rate&quot;: 12.0},   # 100+ seats
]

def get_unit_price(seats):
    for rule in reversed(pricing_table):
        if seats &gt;= rule[&quot;min&quot;]:
            return rule[&quot;rate&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Works in any language. No fancy pattern-matching required — just a loop.&lt;/p&gt;
&lt;h2&gt;Why Bother?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Readable:&lt;/strong&gt; Every rule looks the same, so you’re skimming, not spelunking.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flexible:&lt;/strong&gt; Need a new tier? Tweak the table — no code surgery.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test-Friendly:&lt;/strong&gt; Your test cases &lt;strong&gt;are&lt;/strong&gt; the data. Validate the rows, not a forest of conditionals.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Brain-Friendly:&lt;/strong&gt; Most messy branches are just an implicit table anyway. Make it explicit and move on.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Hot take:&lt;/strong&gt; Nine times out of ten, “business logic” is just a spreadsheet waiting to happen.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Multi-Dimensional? No Problem.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Category     | Origin Group | Band  | Tariff Rate (%)
-------------|--------------|-------|-----------------
Electronics  | FTA          | low   | 2.0
Electronics  | FTA          | mid   | 1.5
Electronics  | FTA          | high  | 0.5
Electronics  | non-FTA      | low   | 5.0
Electronics  | non-FTA      | mid   | 4.0
Electronics  | non-FTA      | high  | 3.0
Apparel      | FTA          | low   | 8.0
Apparel      | FTA          | mid   | 6.0
Apparel      | FTA          | high  | 3.0
Apparel      | non-FTA      | low   | 12.0
Apparel      | non-FTA      | mid   | 10.0
Apparel      | non-FTA      | high  | 8.0
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;tariff_dict = {
    (&quot;electronics&quot;, &quot;FTA&quot;, &quot;low&quot;): 2.0,
    (&quot;electronics&quot;, &quot;FTA&quot;, &quot;mid&quot;): 1.5,
    (&quot;electronics&quot;, &quot;FTA&quot;, &quot;high&quot;): 0.5,
    (&quot;electronics&quot;, &quot;non-FTA&quot;, &quot;low&quot;): 5.0,
    (&quot;electronics&quot;, &quot;non-FTA&quot;, &quot;mid&quot;): 4.0,
    (&quot;electronics&quot;, &quot;non-FTA&quot;, &quot;high&quot;): 3.0,
    (&quot;apparel&quot;, &quot;FTA&quot;, &quot;low&quot;): 8.0,
    (&quot;apparel&quot;, &quot;FTA&quot;, &quot;mid&quot;): 6.0,
    (&quot;apparel&quot;, &quot;FTA&quot;, &quot;high&quot;): 3.0,
    (&quot;apparel&quot;, &quot;non-FTA&quot;, &quot;low&quot;): 12.0,
    (&quot;apparel&quot;, &quot;non-FTA&quot;, &quot;mid&quot;): 10.0,
    (&quot;apparel&quot;, &quot;non-FTA&quot;, &quot;high&quot;): 8.0,
}

# Band classification based on quantity
def resolve_band(quantity):
    if quantity &amp;#x3C; 100:
        return &quot;low&quot;
    elif quantity &amp;#x3C; 1000:
        return &quot;mid&quot;
    else:
        return &quot;high&quot;

# Main lookup function
def find_tariff_rate(category, origin_group, quantity):
    band = resolve_band(quantity)
    key = (category, origin_group, band)
    return tariff_dict.get(key, None)  # None if no match found
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This mindset alone can improve code clarity — even if you don’t fully implement it.&lt;/p&gt;
&lt;h2&gt;Externalize Your Logic&lt;/h2&gt;
&lt;p&gt;With first-class functions in most modern languages, you can also store behaviors — not just constants — in tables and apply them dynamically.&lt;/p&gt;
&lt;p&gt;Because rules are just data, you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Store them&lt;/strong&gt; in JSON, a DB table, or even a Google Sheet.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Let non-devs edit&lt;/strong&gt; promo rates or tax rules without a code push.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skip deploys&lt;/strong&gt; for every tiny tweak. Change data, hit save, done.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Business Impact&lt;/h2&gt;
&lt;h3&gt;From Code to Data&lt;/h3&gt;
&lt;p&gt;The real power? You’re transforming business logic from hardcoded control flow into externalized data. This enables:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Non-engineers to own rule updates&lt;/li&gt;
&lt;li&gt;Logic changes without redeploys&lt;/li&gt;
&lt;li&gt;Lower risk and faster iteration&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Bonus point:&lt;/strong&gt; AI won’t rewrite your logic in a table-driven style unless you ask it to. But if you do, it can refactor it instantly.&lt;/p&gt;
&lt;p&gt;That means table-driven design isn’t just human-friendly. It’s AI-friendly too.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Fewer Tests, Fewer Bugs&lt;/h3&gt;
&lt;p&gt;If your logic is just data, there’s no need to write unit tests checking whether your code correctly re-implements those rules. You just verify the data.&lt;/p&gt;
&lt;h3&gt;Faster Release Cycles&lt;/h3&gt;
&lt;p&gt;Releasing application code is often slow, risky, and tightly coupled to engineering cycles. Logic-as-data enables sub-minute updates with near-zero rollback cost.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Roll forward via code: 0.5 day&lt;/p&gt;
&lt;p&gt;Rollback via infra: 10 min&lt;/p&gt;
&lt;p&gt;Rollback via config/data: 1 min&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Empower Business Users&lt;/h3&gt;
&lt;p&gt;Provide an admin UI. Let domain experts own their logic. Engineering effort drops to zero.&lt;/p&gt;
&lt;h2&gt;⚠️ Caveats to Keep in Mind&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Always include a sensible default or fallback.&lt;/li&gt;
&lt;li&gt;If you change the table’s shape, the surrounding code must change too — plan ahead.&lt;/li&gt;
&lt;li&gt;Data isn’t type-checked at compile time. Add validation or linting for safety.&lt;/li&gt;
&lt;li&gt;Rules-as-data doesn’t protect you from bad rules. Only paranoia survives.&lt;/li&gt;
&lt;li&gt;Memory access is expensive. If you’re squeezing every last nanosecond out of the hot path, pause and benchmark before trading branches for a lookup table.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://specbranch.com/posts/lookup-tables/&quot;&gt;You (Probably) Shouldn&lt;/a&gt; (specbranch.com) - I have been working on another post recently, also related to division, but I wanted to address a comment I got from…&lt;/p&gt;
&lt;h2&gt;Final Thought&lt;/h2&gt;
&lt;p&gt;As &lt;em&gt;Code Complete&lt;/em&gt; reminds us (ch. 18, “Table-Driven Methods”), turning sprawling branch logic into a data table doesn’t just shrink the code you have to reason about. It also slashes the bug surface area and puts the knobs straight into the hands of the people who actually own the rules.&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>台北b-monsterメモ</title><link>https://veritycost.com/posts/taipei-b-monster-memo-e2c4660dbdde/</link><guid isPermaLink="true">https://veritycost.com/posts/taipei-b-monster-memo-e2c4660dbdde/</guid><description>先人の記事はいくつかあります。参考にしつつ補足です</description><pubDate>Wed, 13 Dec 2023 14:08:56 GMT</pubDate><content:encoded>&lt;p&gt;現在台北スタジオはなくなりました。以下過去の参考です&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;先人の記事はいくつかあります。参考にしつつ補足です&lt;/p&gt;
&lt;h2&gt;新光三越の入館方法&lt;/h2&gt;
&lt;p&gt;通常の時間帯なら三越に普通に入るだけです。&lt;/p&gt;
&lt;p&gt;朝方や夜間といった、三越の営業時間外でも入館可能です。入り口は1階にあります。正面入り口は閉ざされているため、新光三越A9の建物をぐるっと回って開いてそうな扉を探しましょう。エレベータホール前に警備員の方がいました。そこからエレベータで5階まで上がります。&lt;/p&gt;
&lt;h2&gt;持ち物&lt;/h2&gt;
&lt;p&gt;手ぶらでOK。日本同様に全部レンタルできます。受付の方にレンタルしたい旨を伝え、現金で支払いましょう。&lt;/p&gt;
&lt;p&gt;2023年現在、台北スタジオではタオルは無料のままです。&lt;/p&gt;
&lt;p&gt;台北スタジオでもプロテイン飲料が買えるようです。台湾のドラッグストアやコンビニでは入手しづらく、必要な方は買ってみましょう。&lt;/p&gt;
&lt;h2&gt;サンドバッグ番号&lt;/h2&gt;
&lt;p&gt;不明なら受付の方に聞く&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>Wigmore Hallから眺める敬称一覧と歴史</title><link>https://veritycost.com/posts/titles-from-wigmore-hall-c468f084/</link><guid isPermaLink="true">https://veritycost.com/posts/titles-from-wigmore-hall-c468f084/</guid><description>ロンドンのWigmore Hallで演奏を聴こうと思い立ち、オンラインで申し込みをしてみました。 すると最初に驚くのが敬称（title）の多さです。</description><pubDate>Sat, 07 Mar 2026 02:36:42 GMT</pubDate><content:encoded>&lt;p&gt;ロンドンのWigmore Hallで演奏を聴こうと思い立ち、オンラインで申し込みをしてみました。
すると最初に驚くのが敬称（title）の多さです。&lt;/p&gt;
&lt;figure class=&quot;media-figure&quot;&gt;
  ![titleのスクロール量](../../assets/wigmore_title.png)
  &lt;figcaption&gt;titleのスクロール量&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;フォームのプルダウンをスクロールすると、軍人・聖職者・貴族・政治家など、実に多様な敬称が並びます。&lt;/p&gt;

























































































































































































































































































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;title&lt;/th&gt;&lt;th&gt;意味&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Mx.&lt;/td&gt;&lt;td&gt;性別を特定しない敬称&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1st Lt&lt;/td&gt;&lt;td&gt;First Lieutenant（陸軍などの中尉）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2nd Lt&lt;/td&gt;&lt;td&gt;Second Lieutenant（少尉）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Air Chief Marshal&lt;/td&gt;&lt;td&gt;英空軍最高階級の一つ（大将級）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Air Commodore&lt;/td&gt;&lt;td&gt;英空軍准将&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Air Marshal&lt;/td&gt;&lt;td&gt;英空軍中将&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Air Vice-Marshal&lt;/td&gt;&lt;td&gt;英空軍少将&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Alderman&lt;/td&gt;&lt;td&gt;市議会の長老議員（歴史的役職）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ambassador&lt;/td&gt;&lt;td&gt;大使&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Baroness&lt;/td&gt;&lt;td&gt;女男爵&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Bishop&lt;/td&gt;&lt;td&gt;司教&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Brig. Gen.&lt;/td&gt;&lt;td&gt;Brigadier General（准将）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Brother&lt;/td&gt;&lt;td&gt;修道士の敬称&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Canon&lt;/td&gt;&lt;td&gt;カノン（大聖堂参事）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cantor&lt;/td&gt;&lt;td&gt;宗教儀式の詠唱者（ユダヤ教など）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Capt.&lt;/td&gt;&lt;td&gt;Captain（大尉／艦長など）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cardinal&lt;/td&gt;&lt;td&gt;枢機卿&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cmdr.&lt;/td&gt;&lt;td&gt;Commander（海軍中佐級）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Colonel&lt;/td&gt;&lt;td&gt;大佐&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Comte&lt;/td&gt;&lt;td&gt;伯爵（フランス語）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Councellor&lt;/td&gt;&lt;td&gt;評議員／参事官&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Count&lt;/td&gt;&lt;td&gt;伯爵&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Countess&lt;/td&gt;&lt;td&gt;伯爵夫人&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Dame&lt;/td&gt;&lt;td&gt;女性ナイト爵位&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Dean&lt;/td&gt;&lt;td&gt;学部長／大聖堂長&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Dowager Viscountess&lt;/td&gt;&lt;td&gt;先代子爵夫人&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Dr.&lt;/td&gt;&lt;td&gt;博士号保持者&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Earl&lt;/td&gt;&lt;td&gt;伯爵（英国貴族）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Estate of&lt;/td&gt;&lt;td&gt;故人の遺産・財産を表す法的表現&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Father&lt;/td&gt;&lt;td&gt;神父&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Flight Lieutenant&lt;/td&gt;&lt;td&gt;英空軍大尉&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Flying Officer&lt;/td&gt;&lt;td&gt;英空軍中尉&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Fr.&lt;/td&gt;&lt;td&gt;Father（神父の略）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Gen.&lt;/td&gt;&lt;td&gt;General（将軍）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Group Captain&lt;/td&gt;&lt;td&gt;英空軍大佐&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;H.R.H.&lt;/td&gt;&lt;td&gt;His/Her Royal Highness（殿下）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Herr&lt;/td&gt;&lt;td&gt;ドイツ語の「Mr.」&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Hon.&lt;/td&gt;&lt;td&gt;The Honourable（名誉称号）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ing.&lt;/td&gt;&lt;td&gt;Engineer（主に欧州の工学称号）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Judge&lt;/td&gt;&lt;td&gt;裁判官&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Justice&lt;/td&gt;&lt;td&gt;上級裁判官&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Lady&lt;/td&gt;&lt;td&gt;貴族女性敬称&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Lord&lt;/td&gt;&lt;td&gt;貴族男性敬称&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Lt.&lt;/td&gt;&lt;td&gt;Lieutenant（中尉）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Lt. Cmdr.&lt;/td&gt;&lt;td&gt;Lieutenant Commander（海軍少佐級）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Lt. Col.&lt;/td&gt;&lt;td&gt;Lieutenant Colonel（中佐）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Maestro&lt;/td&gt;&lt;td&gt;音楽家・指揮者の敬称&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Maj. Gen.&lt;/td&gt;&lt;td&gt;Major General（少将）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Major&lt;/td&gt;&lt;td&gt;少佐&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Marshal of the Royal Air Force&lt;/td&gt;&lt;td&gt;英空軍最高階級&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Mme.&lt;/td&gt;&lt;td&gt;Madame（仏語の既婚女性敬称）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Mms.&lt;/td&gt;&lt;td&gt;複数女性の敬称&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Monsignor&lt;/td&gt;&lt;td&gt;カトリック高位聖職者&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Pilot Officer&lt;/td&gt;&lt;td&gt;英空軍少尉&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Professor&lt;/td&gt;&lt;td&gt;教授&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Rabbi&lt;/td&gt;&lt;td&gt;ラビ（ユダヤ教指導者）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Rear Adm.&lt;/td&gt;&lt;td&gt;Rear Admiral（少将・海軍）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Representative&lt;/td&gt;&lt;td&gt;下院議員&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Revd Canon&lt;/td&gt;&lt;td&gt;聖職者＋大聖堂参事&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Reverend&lt;/td&gt;&lt;td&gt;牧師&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Sen.&lt;/td&gt;&lt;td&gt;上院議員&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Senator&lt;/td&gt;&lt;td&gt;上院議員&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Sgt.&lt;/td&gt;&lt;td&gt;Sergeant（軍曹）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Signor&lt;/td&gt;&lt;td&gt;イタリア語のMr.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Signora&lt;/td&gt;&lt;td&gt;イタリア語のMrs.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Sir&lt;/td&gt;&lt;td&gt;ナイト爵位&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Sister&lt;/td&gt;&lt;td&gt;修道女&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Squadron Leader&lt;/td&gt;&lt;td&gt;英空軍少佐&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Sr.&lt;/td&gt;&lt;td&gt;Senior&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;The Right Hon.&lt;/td&gt;&lt;td&gt;枢密院メンバー等の敬称&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;The Rt. Rev.&lt;/td&gt;&lt;td&gt;Right Reverend（高位司教）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Very Reverend&lt;/td&gt;&lt;td&gt;上級聖職者&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Vice Adm.&lt;/td&gt;&lt;td&gt;Vice Admiral（海軍中将）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Viscount&lt;/td&gt;&lt;td&gt;子爵&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Warrant Officer&lt;/td&gt;&lt;td&gt;准士官&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Wing Commander&lt;/td&gt;&lt;td&gt;英空軍中佐&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;この一覧を眺めていると、イギリス社会の歴史的階層がそのままプルダウンメニューに圧縮されているようにも見えます。&lt;/p&gt;
&lt;p&gt;suffix（Jr. や III など）も入力できるのですが、こちらのリストは省略します。&lt;/p&gt;
&lt;figure class=&quot;media-figure&quot;&gt;![suffixのスクロール量](../../assets/wigmore_suffix.png)&lt;/figure&gt;
&lt;p&gt;さらに住所入力の country（地域） も興味深く、島嶼地域が独立した項目として分かれていたり、「Okinawa」と「Ryukyu Islands」が別項目として存在していました。&lt;/p&gt;
&lt;p&gt;こうしたデータを見ると、単一の最新マスタではなく、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;20世紀初頭の国名リスト&lt;/li&gt;
&lt;li&gt;英国系組織の住所台帳&lt;/li&gt;
&lt;li&gt;旅行業界などの国コード
といった 複数のデータベースを統合した痕跡 があるように感じられます。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;オンラインフォームという何気ないインターフェースですが、
そこには英国社会の制度史と、情報システムの継ぎ足しの歴史が同時に埋め込まれているようです。&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>海外旅行保険の選び方: 何を優先して補償を決めるか</title><link>https://veritycost.com/posts/travel-insurance-basics-2f9c1d4a/</link><guid isPermaLink="true">https://veritycost.com/posts/travel-insurance-basics-2f9c1d4a/</guid><description>海外旅行保険は、補償項目が多く、一見すると「全部つけたほうが安全」に見えます。しかし実際には、保険料に強く影響する項目と、そうでない項目がはっきり分かれています。この記事では、海外旅行保険を選ぶときに迷いやすいポイントを、実務的な優先順位で整理します。</description><pubDate>Sun, 15 Feb 2026 03:00:00 GMT</pubDate><content:encoded>&lt;p&gt;海外旅行保険は、補償項目が多く、一見すると「全部つけたほうが安全」に見えます。しかし実際には、保険料に強く影響する項目と、そうでない項目がはっきり分かれています。この記事では、海外旅行保険を選ぶときに迷いやすいポイントを、実務的な優先順位で整理します。&lt;/p&gt;
&lt;h2&gt;簡単な用語整理&lt;/h2&gt;
&lt;p&gt;意外と混乱しやすいので、最初に用語を押さえておきます。
「誰が、誰に、何を払うのか」を意識すると整理しやすくなります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;保険料：保険契約のために支払うお金
保険金：事故や病気など、保険事故が起きたときに受け取るお金
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;保険料が大きく変わる項目を把握する&lt;/h2&gt;
&lt;p&gt;特に次の項目は、設定すると保険料が大きく跳ね上がりやすい傾向があります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;携行品損害&lt;/li&gt;
&lt;li&gt;旅行キャンセル費用&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これらは、保険金額を少し上げるだけでも保険料が急増します。発生頻度が高く、かつ請求が比較的起こりやすいリスクであるため、保険会社側も慎重な料率設定をせざるを得ないのでしょう。&lt;/p&gt;
&lt;p&gt;一方で、治療・救援費用や個人賠償責任は保険金額を大きく増やしても保険料の増加はわずかです。最悪のケースに対処できる、まさに「保険」の本領発揮です。&lt;/p&gt;
&lt;h2&gt;クレジットカード付帯保険を前提に考える&lt;/h2&gt;
&lt;p&gt;クレジットカード付帯保険と内容が重複している部分を削ることで、保険料は大きく下げられます。携行品損害は重複しやすい項目です。&lt;/p&gt;
&lt;p&gt;一方、クレカ付帯では治療・救援費用が不足気味なことが多い。前述の通り、ここは保険金額を増やしても保険料はあまり増えません。追加契約するなら、むしろここを厚くするのが合理的です。&lt;/p&gt;
&lt;p&gt;実務上よくある構成は、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;携行品損害：クレカ付帯でカバー&lt;/li&gt;
&lt;li&gt;治療・救援費用、賠償責任：海外旅行保険で上乗せ&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;という分担です。複数枚カードを持っている場合は、最も条件の良いものを一枚選ぶだけで十分でしょう。&lt;/p&gt;
&lt;h3&gt;付帯条件を確認する&lt;/h3&gt;
&lt;p&gt;「カード名＋付帯保険」で保険の案内資料を検索し、資料をAIに整理させるとよいでしょう。カードを複数枚保持しているときは全部確認して一番良い条件のものを探しましょう。&lt;/p&gt;
&lt;h3&gt;クレカでの自動付帯/利用付帯&lt;/h3&gt;
&lt;p&gt;利用付帯は「カードを使った場合のみ有効」です。対象となる支払いは航空券に限らず、空港までの公共交通機関が含まれるケースも多い。&lt;/p&gt;
&lt;p&gt;タッチ決済で電車やバスに乗り、そこで利用付帯条件を満たす、という運用も可能です。複数カードを持っている場合、それぞれで利用付帯条件を成立させることもできます。&lt;/p&gt;
&lt;h2&gt;重複補償をさらに削る&lt;/h2&gt;
&lt;p&gt;損害保険は原則として実損てん補です。同じ内容に複数加入しても、損した金額以上は受け取れません。&lt;/p&gt;
&lt;p&gt;死亡保障や傷害後遺障害は、生命保険と重複していることが多い項目で、生命保険の保険金は損害保険と異なり重複して受け取ることが可能です。ただ、海外で亡くなった場合と、日本国内で亡くなった場合とで、冷静に考えると、残された人の生活に関しては経済的になにかが変わるわけではありません。その意味で、海外旅行保険でさらに死亡保障を増やす意義は限定的でしょう。もし不安があるなら生命保険のほうの保障を変更すべきです。&lt;/p&gt;
&lt;p&gt;※ただし、周知の通り治療費については国によって桁が変わるため、治療費に対する保険金はこの限りではありません。&lt;/p&gt;
&lt;h2&gt;ツアー契約時はあえて分割払いを狙う&lt;/h2&gt;
&lt;p&gt;脱線しますが、旅行代理店の破産が時折発生しています。クレジットカードで分割払いにすると、未引き落とし分の引き落としが停止できます(支払停止の抗弁)。金利は増えますが保険料だと考えると検討の余地ありです。&lt;/p&gt;
&lt;h2&gt;さいごに&lt;/h2&gt;
&lt;p&gt;韓国、台湾に数日程度ならクレカの付帯保険で十分という考えもできますが、遠い場所に長くいるなら海外旅行保険は必須でしょう。お得に安心を買いたいところです&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>イギリスのホテル部屋の特徴</title><link>https://veritycost.com/posts/uk-hotel-rooms-3e11da66/</link><guid isPermaLink="true">https://veritycost.com/posts/uk-hotel-rooms-3e11da66/</guid><description>電源はプラグのほか、USB type Aが使えることが多い。時折type Cも利用可能。Type Aは空港など様々な場所で使えて便利。</description><pubDate>Thu, 05 Mar 2026 14:15:44 GMT</pubDate><content:encoded>&lt;p&gt;電源はプラグのほか、USB type Aが使えることが多い。時折type Cも利用可能。Type Aは空港など様々な場所で使えて便利。&lt;/p&gt;
&lt;h2&gt;シャワーとドライヤー&lt;/h2&gt;
&lt;p&gt;天井式のシャワーがよくある。配管の都合で最初は水が出るので距離を取らないと冷たい思いをする。ドライヤーは部屋に付属していないか、あったとしてもクローゼットや引き出しなど浴室以外にある。&lt;/p&gt;
&lt;p&gt;イギリスの電気規格（BS 7671）では浴室内の電源設置が厳しく制限されているため。&lt;/p&gt;
&lt;p&gt;都市ホテルではシャワーのみの部屋も多く、バスタブ付きの部屋は上位カテゴリに設定されていることがある。&lt;/p&gt;
&lt;h2&gt;冷暖房&lt;/h2&gt;
&lt;p&gt;ラジエータ式のセントラルヒーティングが主流なのはヨーロッパ共通。イギリスのホテルでは浴室にあり、タオルウォーマーを兼ねてパイプにタオルが掛けられていることが多い。触ると熱いので気をつけること。その分ヒートショックは少なさそう。&lt;/p&gt;
&lt;p&gt;温度を調整したいなら部屋のどこかにあるダイヤルを探すこと&lt;/p&gt;
&lt;p&gt;冷房はないことが多い。温暖化で今後エアコン付きの部屋が増える可能性も。&lt;/p&gt;
&lt;h2&gt;ベッド・寝具&lt;/h2&gt;
&lt;p&gt;イギリスのベッドの特徴は、高さがあることと、縦幅が小さく感じること。縦幅が通常190cmと世界でも短いほうでありながら、枕が多数置いてある状態でベッドメイクされているので頭の位置が押し出される。枕はサイズや硬さが異なるものが並んでいることが多いので、好みに合わせて使うとよい。ベッドランナーは見かけない。&lt;/p&gt;
&lt;h2&gt;その他&lt;/h2&gt;
&lt;p&gt;トイレに緊急時用のボタンがある。ボタンではなく紐式のこともある。&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>イギリス旅行におけるコインランドリーの使い方</title><link>https://veritycost.com/posts/uk-laundrette-0a4c348e/</link><guid isPermaLink="true">https://veritycost.com/posts/uk-laundrette-0a4c348e/</guid><description>旅行で洗濯を前提にすると持ち物が一気に減って便利です。一つのやり方が現地のコインランドリー活用でしょう。イギリスではクリーニング店laundretteがコインランドリーを併設しているイメージです。なんなら同じ場所同じ機械で店員が洗い物をしていることがあります。特にロンドンで泊まるようなエリアなら近場で探すことも簡単です。</description><pubDate>Wed, 11 Feb 2026 20:20:11 GMT</pubDate><content:encoded>&lt;p&gt;旅行で洗濯を前提にすると持ち物が一気に減って便利です。一つのやり方が現地のコインランドリー活用でしょう。&lt;/p&gt;
&lt;p&gt;イギリスではクリーニング店laundretteがコインランドリーを併設しているイメージです。なんなら同じ場所同じ機械で店員が洗い物をしていることがあります。特にロンドンで泊まるようなエリアなら近場で探すことも簡単です。&lt;/p&gt;
&lt;h2&gt;コインランドリーの使い方&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;£のコインを準備する&lt;/li&gt;
&lt;li&gt;店や機械の注意書きをよく読む。AIを駆使して注意書きの理由も調べると良い&lt;/li&gt;
&lt;li&gt;洗剤がなければその場で洗剤を買う&lt;/li&gt;
&lt;li&gt;洗い物と洗剤を入れ、コインを投入して洗濯&lt;/li&gt;
&lt;li&gt;乾燥機にもコインを投入して乾燥&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;洗濯水温や乾燥機の温度、乾燥時間は洗い物に合わせて適宜調整してください&lt;/p&gt;
&lt;figure class=&quot;media-figure&quot; style=&quot;max-width: 360px; margin: 1rem auto 1.25rem;&quot;&gt;
  ![コインランドリーの洗剤販売機](../../assets/uk-laundrette.jpg)
  &lt;figcaption&gt;洗剤販売機。仕組みが気になる。&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;必要なもの&lt;/h2&gt;
&lt;p&gt;最低限現金 (1ポンドコイン)が必須です。さらに洗剤をその場で買うなら端数が必要になることがあるので確認しておきましょう。洗剤はスーパーでまとめて買っておいたほうが楽でお得な面も&lt;/p&gt;
&lt;p&gt;両替については、店員がいれば対応してくれることがあります。ただし人がいないうえ両替機もないコインランドリー型もありえます。お札しか持ってないようならそのへんの店で何かを購入しましょう。&lt;/p&gt;
&lt;p&gt;クリーニングは小規模業者が多く、機械の更新やキャッシュレス決済導入、決済手数料に耐えられないのかと想像します。&lt;/p&gt;
&lt;h2&gt;Laundrette / Launderetteは本当にコインランドリーか？&lt;/h2&gt;
&lt;p&gt;Laundrette / Launderette は、日本語で言う「コインランドリー」とは必ずしも一致しません。あくまで小規模なクリーニング関連サービスの総称で、業態には幅があります。実際には、以下の3パターンがありえます。&lt;/p&gt;
&lt;h3&gt;1. クリーニング ＋ コインランドリー（最も一般的）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;店員常駐&lt;/li&gt;
&lt;li&gt;セルフ用の洗濯機・乾燥機あり&lt;/li&gt;
&lt;li&gt;同じ店内・同じ機械で、店員が業務での洗濯をしていることもある&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ロンドンなど都市部で最もよく見かける形です。旅行者が「イギリスのコインランドリー」と呼んでいるものは、ほぼこれ。&lt;/p&gt;
&lt;h3&gt;2. クリーニングのみ&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;セルフ洗濯不可&lt;/li&gt;
&lt;li&gt;Service wash（まとめ洗い）や dry cleaning のみ提供&lt;/li&gt;
&lt;li&gt;外観は laundrette だが、コイン投入口が存在しない&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;住宅地寄りのエリアや、小規模店舗で見られます。看板だけで判断すると誤解しやすいタイプ。&lt;/p&gt;
&lt;h3&gt;3. コインランドリーのみ（少数派）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;無人、または営業時間限定で店員がいることもある&lt;/li&gt;
&lt;li&gt;完全セルフサービス&lt;/li&gt;
&lt;li&gt;両替機がないケースもありうる&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;日本の感覚に一番近いですが、英国では相対的に少なめです。&lt;/p&gt;
&lt;h3&gt;検索・見分け方のコツ&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Google Maps の表記
&lt;ul&gt;
&lt;li&gt;launderette → 上記いずれも含みうる&lt;/li&gt;
&lt;li&gt;self-service laundry → パターン1または3の可能性が高い&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;写真から
&lt;ul&gt;
&lt;li&gt;カウンター＋作業台 → クリーニング併設&lt;/li&gt;
&lt;li&gt;大型洗濯機がずらり → セルフ比重高め&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;レビューで
&lt;ul&gt;
&lt;li&gt;たとえばservice wash / staff did my laundry という記述があるか&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;注意事項&lt;/h2&gt;
&lt;p&gt;上記の通り、地域によってはlaundretteが必ずしもセルフサービスを提供しているとは限りません。近くにセルフ式がなければ、service wash（まとめ洗い）としてクリーニングに任せるのも一案です。配達に対応していることもあります。クリーニング料金はコインランドリー型と比べてもそこまで高くない印象です。なお、これも現金のみの可能性があります。&lt;/p&gt;
&lt;p&gt;機械が古い場合、扉が閉まりにくいことがあります。遠慮せず、しっかり押し込みましょう。&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>Why Dependency Reduction Is Still Hard</title><link>https://veritycost.com/en/posts/why-dependency-reduction-is-still-hard-886eb679/</link><guid isPermaLink="true">https://veritycost.com/en/posts/why-dependency-reduction-is-still-hard-886eb679/</guid><description>Dependency reduction improves performance, security, and maintainability, but it’s harder than it looks. This article explores why and how to tackle it in practice.</description><pubDate>Tue, 24 Mar 2026 15:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Including initiatives like &lt;a href=&quot;https://e18e.dev/&quot;&gt;e18e&lt;/a&gt;, reducing dependencies is a highly valuable practice. I’m a big fan of e18e.&lt;/p&gt;
&lt;p&gt;If you remove dependencies, Renovate / Dependabot PRs will decrease.&lt;br&gt;
This reduces the security blast radius and also lowers the burden of investigation and ongoing maintenance.
In addition to improving developer experience, it can also improve production performance.&lt;/p&gt;
&lt;p&gt;That said, the path is not easy. The fact that many repositories still rely on jest makes this quite clear.&lt;br&gt;
These challenges are also largely common across other languages.&lt;/p&gt;
&lt;h1&gt;Common Challenges&lt;/h1&gt;
&lt;h2&gt;Social (Sociotechnical)&lt;/h2&gt;
&lt;p&gt;We meet again.&lt;/p&gt;
&lt;p&gt;As Gerald Weinberg put it, “no matter how it looks at first, it’s always a people problem.”&lt;br&gt;
Dependency reduction is no exception.&lt;/p&gt;
&lt;h3&gt;Low Interest in Non-Functional Concerns&lt;/h3&gt;
&lt;p&gt;Most maintainers and code owners are not deeply interested in dependencies or non-functional requirements themselves.&lt;br&gt;
Given limited time, attention naturally gravitates toward feature development.&lt;/p&gt;
&lt;h3&gt;Lack of Trust in PR Authors&lt;/h3&gt;
&lt;p&gt;PRs from unfamiliar contributors are often ignored or left unreviewed.&lt;br&gt;
This is even more true in the age of AI. Building trust comes first.&lt;/p&gt;
&lt;h3&gt;Different Processes Across Repositories&lt;/h3&gt;
&lt;p&gt;Some repositories accept direct PRs, while others require opening an issue first.&lt;br&gt;
Understanding each repository’s workflow can be burdensome. Even when documented, effective contributions often require reading the “room.”&lt;/p&gt;
&lt;h3&gt;You Still Need to Release&lt;/h3&gt;
&lt;p&gt;Especially for poorly maintained repositories, releasing can be more burdensome than making the change itself.&lt;br&gt;
As long as the project is still in use, you cannot simply merge and walk away.&lt;/p&gt;
&lt;h3&gt;No Real Interest in the Repository Itself&lt;/h3&gt;
&lt;p&gt;If the last update was years ago, or there are no incentives within an organization, improvements are unlikely to move forward.&lt;/p&gt;
&lt;h3&gt;Even with AI, Review Is Still Required&lt;/h3&gt;
&lt;p&gt;If only part of a library is used, it’s possible to rewrite it inline with AI.&lt;br&gt;
This is particularly effective for removing legacy dependencies. However, the review burden still remains significant.&lt;/p&gt;
&lt;h3&gt;Ambiguous Ownership&lt;/h3&gt;
&lt;p&gt;In repositories with multiple maintainers, it is often unclear who is responsible for reviewing dependency-related changes, leading to stagnation.&lt;/p&gt;
&lt;h3&gt;Still Not a Common Practice&lt;/h3&gt;
&lt;p&gt;Dependency reduction is not yet standard practice.&lt;br&gt;
Its value often needs to be explained each time, which adds cognitive overhead.&lt;/p&gt;
&lt;h2&gt;Technical&lt;/h2&gt;
&lt;p&gt;If this were purely a technical problem, it would feel much easier.&lt;/p&gt;
&lt;h3&gt;Ensuring Compatibility Is Hard&lt;/h3&gt;
&lt;p&gt;Even when replacing parts of lodash or moving to native APIs, subtle behavioral differences can occur.&lt;br&gt;
If compatibility is not 100%, uncertainty remains, increasing validation costs or causing PRs to be rejected.&lt;/p&gt;
&lt;h3&gt;Even Minor Upgrades Can Break Things&lt;/h3&gt;
&lt;p&gt;e18e efforts can significantly reduce dependencies, sometimes just through upgrades.&lt;br&gt;
However, even minor version updates can introduce issues, not only within the library itself but also through interactions.&lt;/p&gt;
&lt;h3&gt;Insufficient Test Coverage&lt;/h3&gt;
&lt;p&gt;Tests are often deprioritized.&lt;br&gt;
Without sufficient regression tests, it’s hard to be confident that everything still works.&lt;/p&gt;
&lt;h3&gt;Benefits Are Hard to Measure&lt;/h3&gt;
&lt;p&gt;Removing a single dependency may have only a small performance impact.&lt;br&gt;
Moreover, due to transitive dependencies, removing one explicit dependency may not change the final dependency graph.&lt;/p&gt;
&lt;p&gt;This is common in performance work: meaningful gains often come only after many small improvements are accumulated.
However, improving the entire ecosystem to achieve that is time-consuming.&lt;/p&gt;
&lt;h3&gt;After the Easy Wins, Only Hard Dependencies Remain&lt;/h3&gt;
&lt;p&gt;After easy fixes, what remains are complex dependencies.&lt;br&gt;
Fixing A may require changes to B, which in turn affects C and D.&lt;/p&gt;
&lt;h3&gt;Hard to Understand Impact Scope&lt;/h3&gt;
&lt;p&gt;Especially in monorepos, it takes time to determine how far the impact of a dependency removal extends.&lt;br&gt;
Even seemingly unrelated scripts can be affected.&lt;/p&gt;
&lt;h3&gt;Dependencies Come Back&lt;/h3&gt;
&lt;p&gt;Even after significant effort to reduce them, dependencies can quickly accumulate again.&lt;/p&gt;
&lt;h1&gt;Recommended Approaches&lt;/h1&gt;
&lt;h2&gt;Remove Completely&lt;/h2&gt;
&lt;p&gt;If a tool like &lt;a href=&quot;http://knip.dev/&quot;&gt;knip&lt;/a&gt; confirms that a dependency can be removed without impact, this is the simplest approach.&lt;br&gt;
It is effectively risk-free.&lt;/p&gt;
&lt;h2&gt;Make Small, Incremental Changes&lt;/h2&gt;
&lt;p&gt;Smaller PRs are easier to review and help build trust.&lt;br&gt;
However, this increases the number of reviews and releases required.&lt;/p&gt;
&lt;h2&gt;Replace with AI-Generated Implementations&lt;/h2&gt;
&lt;p&gt;A more aggressive approach, but it can often be effective.
Breaking away from legacy constraints and rebuilding from scratch can sometimes be faster.&lt;br&gt;
That said, adoption and community considerations require care.&lt;/p&gt;
&lt;h2&gt;Prevent Reintroduction&lt;/h2&gt;
&lt;p&gt;Reducing dependencies is not enough. You also need to prevent them from creeping back in.&lt;br&gt;
Tools like the following can help establish guardrails:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/e18e/action-dependency-diff&quot;&gt;GitHub - e18e/action-dependency-diff&lt;/a&gt; (github.com) - GitHub Action to show dependency diff in pull requests and help catch dependency creep.&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Even when the goal is simply to remove dependencies, the same kinds of challenges found in broader software development inevitably emerge.&lt;/p&gt;
&lt;p&gt;A future where AI solves all of this doesn’t seem particularly realistic. And even if it did, whether our electricity bills go down or up is a different question.&lt;/p&gt;
&lt;p&gt;P.S. I also built a small tool in this area:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://package-replacement.veritycost.com/&quot;&gt;Package Replacement&lt;/a&gt; (package-replacement.veritycost.com) - Copy-first registry for vendoring small package behavior into your repo.&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>なぜ依存関係の削減は難しいのか</title><link>https://veritycost.com/posts/why-dependency-reduction-is-still-hard-ja-c47e723f/</link><guid isPermaLink="true">https://veritycost.com/posts/why-dependency-reduction-is-still-hard-ja-c47e723f/</guid><description>依存関係削減はパフォーマンスやセキュリティ、保守性を改善します。しかし実際にはなぜ難しいのか。その理由と実践的な進め方を整理します。</description><pubDate>Tue, 24 Mar 2026 15:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://e18e.dev/&quot;&gt;e18e&lt;/a&gt;も含め、依存関係の削減は素晴らしい取り組みです。&lt;/p&gt;
&lt;p&gt;依存関係を削除すれば、Renovate / Dependabot のPRは減ります。
セキュリティ上の影響範囲が小さくなるのはもちろん、調査や追従の負担も減ります。
開発体験の改善に加え、本番パフォーマンスが向上することもあります。&lt;/p&gt;
&lt;p&gt;とはいえ、道のりは平坦ではありません。いまだに多くのリポジトリがjestのままであることからも、それはよく分かります。なお、こうした課題は他の言語でもかなり共通しています&lt;/p&gt;
&lt;h1&gt;よくある課題&lt;/h1&gt;
&lt;h2&gt;社会的なもの&lt;/h2&gt;
&lt;p&gt;まずはこれ。というかこれがすべて。&lt;/p&gt;
&lt;h3&gt;機能以外に関心が向きにくい&lt;/h3&gt;
&lt;p&gt;ほとんどのメンテナやコードオーナーは、依存関係や非機能要件そのものには強い関心を持っていません。
忙しさもあり、関心はどうしても機能追加に集中しがちです。&lt;/p&gt;
&lt;h3&gt;PR作成者への信頼がない&lt;/h3&gt;
&lt;p&gt;よく知らない人からのPRは、レビューされずに流れてしまうことも少なくありません。
AIの時代はなおさらです。まずは信頼を得る必要があります。&lt;/p&gt;
&lt;h3&gt;リポジトリごとに進め方が違う&lt;/h3&gt;
&lt;p&gt;そのままPRを送ってよいリポジトリもあれば、先にissueを立てる必要があるものもあります。
やり方を読み解くだけでも負担です。書いてあるだけまだ親切で、実際には空気を読む必要があることも多いでしょう。&lt;/p&gt;
&lt;h3&gt;結局リリースしないといけない&lt;/h3&gt;
&lt;p&gt;とくにほぼ保守されていないリポジトリでは、変更そのものよりもリリースが負担になります。
使われている限り、改善して終わりにはできません。&lt;/p&gt;
&lt;h3&gt;そもそも該当リポジトリに関心がない&lt;/h3&gt;
&lt;p&gt;最終更新が何年も前だったり、組織内でインセンティブがなかったりすると、改善は進みません。&lt;/p&gt;
&lt;h3&gt;AIを使っても結局レビューは必要&lt;/h3&gt;
&lt;p&gt;外部ライブラリの一部だけを使っている場合、AIでinline実装を書き下ろすことも可能です。
とくにレガシー依存の削除には有効です。ただし、最終的なレビューの負担は依然として重く残ります。&lt;/p&gt;
&lt;h3&gt;オーナーシップが曖昧&lt;/h3&gt;
&lt;p&gt;複数メンテナのリポジトリでは、依存関係の変更を誰がレビューするのか定まっていないことが多く、結果として放置されがちです。&lt;/p&gt;
&lt;h3&gt;やっている人がまだ少ない&lt;/h3&gt;
&lt;p&gt;依存関係削減はまだ開発の常識とまでは言えず、毎回その意義を説明する必要があります。理解コストが高いのも課題です。&lt;/p&gt;
&lt;h2&gt;技術的なもの&lt;/h2&gt;
&lt;p&gt;AIになんとかしてほしいもの。&lt;/p&gt;
&lt;h3&gt;現実的に互換性の担保が難しい&lt;/h3&gt;
&lt;p&gt;lodashの部分置き換えやネイティブAPIへの移行でも、微妙に仕様がずれることがあります。
100%互換でないと不安が残り、検証コストが増えたり、PRが見送られたりします。&lt;/p&gt;
&lt;h3&gt;バージョンアップでも不具合は起こりうる&lt;/h3&gt;
&lt;p&gt;e18e対応により依存関係が一気に減ることがありますが、マイナーバージョンアップであっても不具合が出る可能性は否定できません。
ライブラリ単体だけでなく、相互作用が原因になることも多いです。&lt;/p&gt;
&lt;h3&gt;テストが薄く、自信を持ちにくい&lt;/h3&gt;
&lt;p&gt;テストは後回しになりがちです。
リグレッションテストが不足していると、「本当に大丈夫か」を確信できません。&lt;/p&gt;
&lt;h3&gt;効果が分かりづらい&lt;/h3&gt;
&lt;p&gt;単一ライブラリの削減では、パフォーマンス改善のインパクトが小さいことがあります。
また、transitive dependencyの影響で、明示的な依存を削っても最終的な構造が変わらない場合もあります。&lt;/p&gt;
&lt;p&gt;パフォーマンス改善ではよくあることですが、小さな改善を積み重ねて初めて大きな効果が現れることがあります。
しかし、そのためにエコシステム全体を改善していくのは手間がかかります。&lt;/p&gt;
&lt;h3&gt;複雑なものしか残っていない&lt;/h3&gt;
&lt;p&gt;単純な修正の後に残るのは複雑な依存です。
Aを直すためにB、そのためにCとDを…というドミノ的な修正が発生します。&lt;/p&gt;
&lt;h3&gt;影響範囲に自信を持ちづらい&lt;/h3&gt;
&lt;p&gt;とくにモノリポでは、依存削除の影響がどこまで及ぶかの把握に時間がかかります。無関係に見えるscript類もやっかいです&lt;/p&gt;
&lt;h3&gt;削減しても再び増える&lt;/h3&gt;
&lt;p&gt;大きな労力をかけて削減しても、依存関係は簡単に再び増えていきます。&lt;/p&gt;
&lt;h1&gt;やり方おすすめ&lt;/h1&gt;
&lt;h2&gt;完全に削除する&lt;/h2&gt;
&lt;p&gt;knipなどで影響範囲なく削除できる場合は最も簡単です。文字通りノーリスクです。&lt;/p&gt;
&lt;h2&gt;小さく分けて送る&lt;/h2&gt;
&lt;p&gt;レビューしやすく、信頼も得やすくなります。ただしレビューとリリース回数は増えます。&lt;/p&gt;
&lt;h2&gt;AIで代替実装を作る&lt;/h2&gt;
&lt;p&gt;大技ですが有効です。古いしがらみを断ち、新しく作り直したほうが進むこともあります。
ただし採用やコミュニティとの関係には注意が必要です。&lt;/p&gt;
&lt;h2&gt;予防する&lt;/h2&gt;
&lt;p&gt;依存関係は削るだけでなく、増やさない仕組みも重要です。
以下のようなツールでガードレールを設けるとよいでしょう。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/e18e/action-dependency-diff&quot;&gt;GitHub - e18e/action-dependency-diff&lt;/a&gt; (github.com) - PR上で依存差分を可視化し、依存の増加を検知しやすくするGitHub Action。&lt;/p&gt;
&lt;h1&gt;最後に&lt;/h1&gt;
&lt;p&gt;依存関係の削除であっても結局他の開発と同じような課題が浮かび上がります。AIが全部やってほしいですね&lt;/p&gt;
&lt;p&gt;P.S. このあたりに関連して、こんなツールも作りました。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://package-replacement.veritycost.com/&quot;&gt;Package Replacement&lt;/a&gt; (package-replacement.veritycost.com) - Copy-first registry for vendoring small package behavior into your repo.&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item><item><title>The Titles in Wigmore Hall&apos;s Booking Form</title><link>https://veritycost.com/en/posts/wigmore-hall-dropdown-tiny-archive-1444915a/</link><guid isPermaLink="true">https://veritycost.com/en/posts/wigmore-hall-dropdown-tiny-archive-1444915a/</guid><description>Wigmore Hall&apos;s booking form includes an unusually long list of titles, suffixes, and country names, some of which appear to reflect older systems and conventions.</description><pubDate>Tue, 18 Aug 2026 10:18:03 GMT</pubDate><content:encoded>&lt;p&gt;I was booking a concert at Wigmore Hall in London when I noticed something unusual in the booking form: the title dropdown was extremely long.&lt;/p&gt;
&lt;p&gt;It didn’t stop at Mr, Ms, and Dr. It included military ranks, aristocratic titles, religious titles, diplomatic titles, and several titles I had never seen before.&lt;/p&gt;
&lt;figure class=&quot;media-figure&quot;&gt;
  ![A long list of titles in the Wigmore Hall booking form](../../assets/wigmore_title.png)
  &lt;figcaption&gt;The title dropdown in Wigmore Hall&apos;s booking form&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Some examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Air Chief Marshal&lt;/li&gt;
&lt;li&gt;Dowager Viscountess&lt;/li&gt;
&lt;li&gt;Monsignor&lt;/li&gt;
&lt;li&gt;The Right Hon.&lt;/li&gt;
&lt;li&gt;Wing Commander&lt;/li&gt;
&lt;li&gt;H.R.H.&lt;/li&gt;
&lt;li&gt;Estate of&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The suffix field is similarly extensive, combining academic degrees, honours, and post-nominal letters.&lt;/p&gt;
&lt;figure class=&quot;media-figure&quot;&gt;
  ![A long list of suffixes in the Wigmore Hall booking form](../../assets/wigmore_suffix.png)
  &lt;figcaption&gt;The booking form also contains an extensive suffix list&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;Why so many titles?&lt;/h2&gt;
&lt;p&gt;Wigmore Hall has been around since 1901. Over that time, it has dealt with performers, patrons, donors, diplomats, clergy, military officers, and members of the aristocracy.&lt;/p&gt;
&lt;p&gt;Many of the entries are forms of address that are still used in Britain.&lt;/p&gt;
&lt;p&gt;“Wing Commander,” for example, is an RAF rank. “The Right Hon.” is a style used by certain senior politicians and members of the peerage. “Monsignor” is an honorific used for some Catholic clergy. “Dowager Viscountess” refers to the widow of a viscount who retains the title after it passes to a successor.&lt;/p&gt;
&lt;p&gt;Some entries are harder to classify. “Estate of,” for example, isn’t a title at all. This suggests that the field is used for more than choosing how to address a person.&lt;/p&gt;
&lt;p&gt;The list may simply reflect the types of records the booking system has needed to handle over time.&lt;/p&gt;
&lt;h2&gt;The country list is odd too&lt;/h2&gt;
&lt;p&gt;The address form contains another unusual list. Both “Okinawa” and “Ryukyu Islands” appear as separate entries in the country or region field.&lt;/p&gt;
&lt;p&gt;I don’t know the source of this data, so I can’t say why both entries exist. The list could come from an old address database, or it could combine data from several sources.&lt;/p&gt;
&lt;p&gt;Either way, “Ryukyu Islands” is an unusual entry in a current address form.&lt;/p&gt;
&lt;p&gt;We usually associate legacy software with old code or infrastructure. But lists of titles, countries, regions, and other categories can also remain in a system for years.&lt;/p&gt;
&lt;p&gt;Removing an entry can be difficult. Existing customer records may use it. Another system may expect it. In some cases, nobody may know why the entry was added in the first place.&lt;/p&gt;
&lt;p&gt;As a result, a dropdown can contain information from several generations of a system.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This is the English version of the &lt;a href=&quot;https://veritycost.com/posts/titles-from-wigmore-hall-c468f084/&quot;&gt;original Japanese article&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content:encoded><author>ask_yoshi_taka</author></item></channel></rss>