前回の記事で、LINEコスト予測のロジックに根本的な問題があることを発見し、「コストを日割り予測する」方式から「メッセージ数を予測してから料金を計算する」方式に設計を修正しました。
設計を修正して、実装して、デプロイして。ここで「終わり」にしてしまいがちなのですが、正直なところ、段階的料金の計算が本当に合っているのか、少し不安でした。15段階のティア構造を正しく実装できている確信が持てなかったのです。
そこで、実際にレポートが出力された数値を見ながら、手計算で答え合わせをすることにしました。
検証のきっかけ:「本当に合ってる?」という不安
修正後のモニタリングレポートを眺めていたとき、ふと思ったのです。
「このコスト予測、本当に正しく計算されているのか?」
前回の問題は、ロジックの誤りが長期間見過ごされていたことでした。修正後のロジックだって、同じことが起きないとは限りません。特に、15段階の料金体系は複雑です。ティアの境界で計算がずれていたり、端数処理が間違っていたりする可能性は十分あります。
意外とここが肝です。「修正した」と「正しく動いている」は別の話です。
検証の方針:何を、どうやって確認するか
検証するにあたって、まず何を確認すべきか整理しました。
検証したいポイントは3つです。1つ目は、段階的料金計算が正しいか。ティアの境界をまたぐケースで、各ティアの通数と単価が正しく適用されているか。2つ目は、メッセージ数予測のロジック。日割り計算の式自体はシンプルですが、経過日数の数え方が合っているか。3つ目は、月の日数の扱い。2月は28日、3月は31日。これが正しく反映されているか。
graph TD
A[レポートの出力値を取得] --> B[手計算で同じ値を再現]
B --> C{一致するか?}
C -->|Yes| D[ロジックは正しい]
C -->|No| E[どこでズレているか特定]
E --> F[ティア計算?経過日数?月の日数?]
方法はシンプルです。レポートが出力した値を取得して、同じ入力値で手計算して、結果が一致するかを確認する。地味ですが、これが一番確実です。
実データでの検証
前提条件の確認
検証した日の状況はこうでした。
- 日付:月初から5日目(2月)
- 実績メッセージ数:34,666通
- 2月の日数:28日
まず、この時点で無料枠(30,000通)を超えていることがわかります。つまり、4,666通が課金対象になっているはずです。
ステップ1:実績コストの手計算
総メッセージ数: 34,666通
無料枠: 30,000通
課金対象: 4,666通(= 34,666 - 30,000)
Tier 1 適用: 4,666通 × 3.0円/通 = 13,998円
月額固定費: 15,000円
メッセージ代: 13,998円
合計: 28,998円
レポートの出力値と一致。ここまではOKです。
ステップ2:予測メッセージ数の計算
予測メッセージ数 = (実績メッセージ数 ÷ 経過日数) × 月の総日数
= (34,666 ÷ 5) × 28
= 6,933.2 × 28
= 194,129.6
≈ 194,130通(四捨五入)
5日間で約3.5万通ということは、1日平均で約6,933通。このペースが月末まで続くと約19.4万通になる見込みです。
ステップ3:予測コストの手計算(ここが本番)
194,130通の場合、課金対象は164,130通(= 194,130 - 30,000)。これを15段階の料金テーブルに当てはめます。
【無料枠】 30,000通 → 0円
【Tier 1】 1〜50,000通 → 単価 3.0円
50,000通 × 3.0円 = 150,000円
残り: 164,130 - 50,000 = 114,130通
【Tier 2】 50,001〜100,000通 → 単価 2.8円
50,000通 × 2.8円 = 140,000円
残り: 114,130 - 50,000 = 64,130通
【Tier 3】 100,001〜200,000通 → 単価 2.6円
64,130通 × 2.6円 = 166,738円
残り: 0通
メッセージコスト合計: 456,738円
月額固定費: 15,000円
予測コスト合計: 471,738円
レポートの出力値:471,738円。一致しました。
正直、ティアの境界をまたぐ計算が合っているか心配だったのですが、ちゃんと動いています。Tier 1で50,000通を使い切り、Tier 2でさらに50,000通、残りの64,130通がTier 3に入る。この「残りの通数」の処理が正しく実装されていることを確認できました。
内訳を表にまとめる
| ティア | 範囲 | 使用通数 | 単価 | 小計 |
|---|---|---|---|---|
| 無料枠 | 0〜30,000 | 30,000通 | - | ¥0 |
| Tier 1 | 〜50,000 | 50,000通 | ¥3.0 | ¥150,000 |
| Tier 2 | 〜100,000 | 50,000通 | ¥2.8 | ¥140,000 |
| Tier 3 | 〜200,000 | 64,130通 | ¥2.6 | ¥166,738 |
| 月額固定費 | - | - | - | ¥15,000 |
| 合計 | - | 194,130通 | - | ¥471,738 |
もうひとつの検証:月の日数は正しいか
計算が合っていることは確認できましたが、もうひとつ気になることがありました。「2月は28日で計算されているけど、3月になったらちゃんと31日で計算されるのか?」
実装を確認したところ、Pythonの標準ライブラリ calendar.monthrange() を使って月の日数を取得していました。
from calendar import monthrange
days_in_month = monthrange(ref.year, ref.month)[1]
この関数は、年と月を渡すと、その月の日数を正しく返してくれます。閏年も自動判定です。
monthrange(2026, 2)[1] # → 28(平年)
monthrange(2026, 3)[1] # → 31
monthrange(2024, 2)[1] # → 29(閏年)
つまり、月が変わっても日数が正しく反映されることは、言語の標準ライブラリが保証してくれています。ここは安心できるポイントでした。
ただし、「経過日数」の数え方には注意が必要です。実装では days_passed = max(now.day - 1, 0) としていて、当日を含めない計算になっています。これは「前日までの実績データを使って予測する」という設計意図に基づいています。
graph LR
A[2月1日] --> B[2月2日]
B --> C[2月3日]
C --> D[2月4日]
D --> E[2月5日]
E --> F["2月6日(今日)"]
style A fill:#e8f5e9
style B fill:#e8f5e9
style C fill:#e8f5e9
style D fill:#e8f5e9
style E fill:#e8f5e9
style F fill:#fff4e1
2月6日時点で days_passed = 5(1日〜5日の実績)。これも意図通りでした。
同じ実績データで3月だったら?
せっかくなので、同じ実績データ(5日で34,666通)が3月だった場合にどうなるかも計算してみました。
予測メッセージ数 = (34,666 ÷ 5) × 31 = 214,849.2 ≈ 214,849通
2月の194,130通に対して、3月は214,849通。日数が3日多いだけで、約2万通の差が出ます。当然、予測コストも変わってきます。
これは「月の日数が正しく反映される」ことが重要な理由でもあります。もし28日固定で計算していたら、3月の予測が常に過小になるわけです。
検証を通して気づいたこと
「合ってる」を確認するのは、意外と手間がかかる
正直、「手計算で検証する」と言うと簡単に聞こえるのですが、実際にやってみると意外と時間がかかります。ティアの境界を正しく理解して、1つずつ積み上げて計算して、端数処理の方法を確認して。
でも、この手間をかけることで「この実装は正しい」と自信を持って言えるようになりました。前回の問題が「長期間見過ごされていた」ことを考えると、この手間は投資として悪くないと思っています。
検証スクリプトという選択肢
今回は手計算で検証しましたが、同じロジックを独立した検証スクリプトとして実装するアプローチもあります。本体の実装とは別に、同じ入力から同じ出力が得られることを自動で確認できるようにしておく。
ただし、検証スクリプト自体が間違っている可能性もあるので、最初の1回は手計算で確認して、その結果をテストケースとして検証スクリプトに組み込むのが確実です。
「何を検証するか」の選び方
すべてのパターンを検証するのは現実的ではありません。今回は以下の観点で検証対象を選びました。
- 実データ:実際にレポートが出力した値で検証(架空のデータではなく)
- 境界をまたぐケース:ティアの境界を超えるケースが含まれている実データを選ぶ
- エッジケース:月の日数の違い(28日 vs 31日)、経過日数の数え方
特に「実データで検証する」というのは重要です。テストコードで用意した綺麗なデータでは通るけど、実データでは通らない、ということは珍しくありません。
まとめ
修正後のLINEコスト予測ロジックを、実データを使って手計算で検証しました。
確認できたこと:
- 段階的料金計算(15段階ティア)が正しく動作している
- メッセージ数ベースの予測ロジックが意図通りに機能している
- 月の日数が
calendar.monthrange()で正しく処理されている - 経過日数の数え方が設計意図に合っている
前回の記事では「予測対象を間違えていた」という設計の問題を見つけました。今回はその修正が「本当に正しく動いているか」を確認した話です。
設計を直すことと、直した結果を検証すること。この2つはセットです。特に、料金計算のような「間違っていても動いてしまう」タイプの処理は、意識的に検証の時間を取らないと、次の問題発見がまた「違和感」頼みになってしまいます。
地味な作業ですが、「合ってる」と言い切れる状態を作ることには、それだけの価値があると思っています。
参考:LINE API について学ぶ
LINEメッセージAPIの料金体系や仕様について深く理解したい場合は、以下のような実践ガイドが参考になります。
[📦 商品リンク: moshimo-card-TUBHo]
実際の実装と公式ドキュメントを照らし合わせながら進めることで、今回のような料金計算の細かい仕様も正確に実装できるようになります。