【テクニカル・上級編】Notionの「マルチセレクト」と「数式プロパティ」を組み合わせたプロジェクト工数・予算の動的バーンダウンチャート作成術 – プロジェクト・ナレッジ管理活用バイブル

Notionネイティブ限界突破:マルチセレクトとFormula 2.0が生む「ゼロ外部依存」動態バーンダウンチャートの極意

開発現場のベロシティを殺す最大の癌、それは「情報のサイロ化」と「ツールのコンテキストスイッチ」だ。Jiraを開き、Googleスプレッドシートを開き、Lookerでダッシュボードを眺める――。この無駄な儀式に、エンジニアの認知リソースを毎スプリント何時間ドブに捨てているのか?

断言しよう。外部のBIツールやグラフ描画プラグインに依存している時点で、君たちのドキュメント設計は敗北している。

NotionのFormula 2.0とマルチセレクトプロパティを極限までチューニングすれば、外部ツールの一切を排除し、データベースのビュー単体でリアルタイムな工数消費・予算消化のバーンダウンチャート、および高精度な進捗プログレスバーを完結させることが可能だ。

本稿では、数式演算のパフォーマンス最適化から、視覚的認知負荷を極限まで下げたUIハックまで、Notionを骨の髄まで掌握するアーキテクトのための実装全書を公開する。

—

1. データモデル設計:なぜ「マルチセレクト」なのか?

一般的なNotionのプロジェクト管理では、ステータスを単一の「ステータス(Select)」で管理しがちだ。しかし、これでは「どのスプリントで、どのロール(FE/BE/QA)の工数が何人日消費されたか」という多次元のマトリクスをネイティブで表現できない。

ここで登場するのがマルチセレクトプロパティだ。これを時系列(スプリント)やコンポーネントの軸として強制適用する。

データベーススキーマ要件定義

| プロパティ名 | 型 | 設計意図・制約 |
| :— | :— | :— |
| `Name` | タイトル | タスク名 |
| `Sprint` | マルチセレクト | 複数スプリントに跨るタスクのトレース(例: `Sprint-01`, `Sprint-02`) |
| `Estimate` | 数学(Number) | 予定工数(Story Points or 人日) |
| `Actual` | 数学(Number) | 実績工数 |
| `Budget` | 数学(Number) | 予算アロケーション(コスト換算用) |
| `State` | ステータス | `Not started` / `In progress` / `Done` |

> アーキテクトの知見:
> 単一セレクトではなくマルチセレクトを採用する理由は、将来的な「スプリント間の工数再配分シミュレーション」をFormulaから動的に引くためだ。配列としてのスプリント構造を保持することで、のちの数式プロパティでの集計クエリが劇的にシンプルになる。

—

2. Formula 2.0による動的バーンダウンの数学的構築

外部のグラフ描画エンジンを使えない制約下で、いかにして視覚的なバーンダウンを表現するか? 答えは「テキストベースのプログレスバー(ASCII/Unicode Art)」の動的生成と、残余工数(Remaining Work)のリアルタイム演算である。

以下のFormulaを「BurnDown Status」という数式プロパティに流し込めば、スプリントごとの残余工数が視覚的なグラフとしてレンダリングされる。

完全自動算出・バーンダウン描画Formula (Formula 2.0)

// — [Constants & Aggregations] —
let(
// データベース全体の総予定工数を算出
totalEstimate, prop(“Estimate”).sum(),
// 完了済みタスクの工数を算出
completedWork, filter(current.database, v.State == “Done”).map(current.Estimate).sum(),
// 残余工数
remaining, totalEstimate – completedWork,

// — [Visual Renderer: Progress Bar] —
// グラフの最大セグメント数(幅)
barLength, 10,
// 完了率に応じたブロック数を計算(ゼロ除算ガード付き)
progressRatio, if(totalEstimate == 0, 0, completedWork / totalEstimate),
filledBlocks, round(progressRatio barLength),
emptyBlocks, barLength – filledBlocks,

// Unicodeブロック文字による描画
bar, “█”.repeat(filledBlocks) + “░”.repeat(emptyBlocks),

// — [Velocity & Burn Health Check] —
// 消化率パーセンテージ
pct, round(progressRatio 100),

// 状態に応じたインジケータ(絵文字による視認性向上)
indicator, if(pct >= 100, “🟢 完了”, if(pct >= 50, “🟡 順調”, “🔴 遅延リスク”)),

// — [Output Composition] —
bar + ” ” + pct + “% (” + remaining + “pt残) ” + indicator
)

この数式が叩き出すUX

この数式を適用したプロパティをNotionの「ボードビュー」や「ギャラリービュー」のプレビューに配置すると、追加のプラグインなしで以下のようなリッチな出力をリアルタイムで得られる。

`████████░░ 80% (4pt残) 🟡 順調`

データベースの数値を変更した瞬間、ブラウザのJavaScriptエンジン(Notionクライアント側)でミリ秒単位で再計算され、ダッシュボード全体が同期する。

—

3. 予算消化とバーンレート(Burn Rate)の自動監視

エンジニアリングマネージャー(EM)やCTOが最も気にするのは「工数」ではなく「予算の消化スピード(Burn Rate)」だ。マルチセレクトで指定されたスプリントごとのコストを自動集計し、予算オーバーランを検知するロジックを実装する。

コスト・予算監視Formula

let(
// 予算総額と実績コストの集計
totalBudget, prop(“Budget”).sum(),
totalActualCost, prop(“Actual”).sum(),

// 消化率
costRatio, if(totalBudget == 0, 0, totalActualCost / totalBudget),
costPct, round(costRatio 100),

// アラートロジック:予算の90%を超過かつ未完了の場合に警告
isOverrunRisk, costRatio > 0.9,

// 出力生成
“💰 予算消化: ” + costPct + “% [” + totalActualCost + ” / ” + totalBudget + “円] ” +
if(isOverrunRisk, “⚠️ 予算枯渇警告”, “✨ 健全”)
)

これをロールアッププロパティやプロジェクト管理親アイテム(Parent Item)に集約することで、エピック単位、スプリント単位での財務的健全性が一目で担保される。

—

4. パフォーマンス最適化ハック:Notionの「重さ」を消し去る技術

高機能なFormula 2.0やマルチセレクトを多用したデータベースは、データ量が数千件を超えると「Notion特有の重さ(クライアントサイド・レンダリングの肥大化)」を引き起こす。

現場のベロシティを落とさないために、以下の低レイヤ最適化を必ず施せ。

1. フィルターのスコープを絞る(Viewの分割)

Notionは全レコードをメモリ上にロードしてからフィルターをかける挙動を示すことがある。

  • 対策: 「アーカイブ済みスプリント」のレコードは、自動化または手動で別データベースへアーカイビング(または「Status = Archived」でフィルタリングし、デフォルトビューから完全除外)する。アクティブなスプリントのレコード数常時200件以下に抑えるのがパフォーマンスの黄金律である。

2. リレーションとロールアップの多重ネストを避ける

Formulaの中で他のデータベースへのリレーションを何段階も辿ると、N+1問題に近いクエリ爆発を起こし、UIがフリーズする。

  • 対策: 必要なデータは極力同一データベース内にマルチセレクトや数値プロパティとして持ち、ロールアップの多段経由を排除する。

—

5. 高度な自動化:Notion APIとCLIを用いたスプリント切り替えの完全自動化

ここまで作り込んだダッシュボードも、スプリント終了時の「マルチセレクトの付け替え」や「ステータスリセット」を手動でやっているようではエンジニアの恥だ。

GitHub Actionsや社内ニッチなCronサーバーからNotion APIを叩き、スプリントの切り替えとバーンダウンのアーカイブを完全に自動化するTypeScriptスクリプトを提示する。

スプリント自動ロールオーバー・スクリプト (`rollover-sprint.ts`)

import { Client } from “@notionhq/client”;

// 環境変数からのセキュアな初期化
const notion = new Client({ auth: process.env.NOTION_API_KEY });
const DATABASE_ID = process.env.NOTION_DATABASE_ID!;

async function rolloverSprint(oldSprint: string, newSprint: string) {
try {
// 1. 旧スプリントで未完了のタスクを特定
const response = await notion.databases.query({
database_id: DATABASE_ID,
filter: {
and: [
{
property: “Sprint”,
multi_select: { contains: oldSprint },
},
{
property: “State”,
status: { does_not_equal: “Done” },
},
],
},
});

console.log(`[Info] 持ち越し対象タスク数: ${response.results.length}件`);

// 2. トランザクション的に次スプリントへマルチセレクトを更新(キャリーオーバー処理)
for (const page of response.results) {
const currentMultiSelect = (page as any).properties.Sprint.multi_select;
// 古いスプリントを外し、新しいスプリントを追加
const updatedSelects = currentMultiSelect
.filter((s: any) => s.name !== oldSprint)
.concat({ name: newSprint });

await notion.pages.update({
page_id: page.id,
properties: {
Sprint: {
multi_select: updatedSelects,
},
},
});
console.log(`[Synced] Page ID: ${page.id} -> 移行完了 (${newSprint})`);
}
} catch (error) {
console.error(“[Error] スプリントのロールオーバーに失敗しました:”, error);
process.exit(1);
}
}

// 実行例: Sprint-01 から Sprint-02 へ未完了タスクをキャリーオーバー
rolloverSprint(“Sprint-01”, “Sprint-02”);

これをCI/CDパイプライン(毎週月曜の深夜など)に組み込むことで、人間が管理画面を触る必要すらなくなる。情報伝達の遅延はゼロになり、開発チームはコードを書くことだけに集中できる。

—

結言

ツールは使い手を選ぶ。汎用的な「メモ帳」としてNotionを扱っているうちは、単なる情報のゴミ捨て場にすぎない。しかし、データモデルの本質を理解し、Formula 2.0とAPIを組み合わせることで、Notionは最強の動的アジャイル・コントロールセンターへと変貌を遂げる。

外部ツールへの依存を断ち切り、自分たちの手で開発環境のインフラストラクチャをハックせよ。それこそが、真のハイパフォーマンス・エンジニアリングである。

タイトルとURLをコピーしました