問題
Astro のページファイル(.astro)でフロントマター内に定義した定数を、export async function getStaticPaths() から参照すると ReferenceError が発生する。
---
const ITEMS_PER_PAGE = 10;
export async function getStaticPaths() {
const pageSize = ITEMS_PER_PAGE; // ❌ ReferenceError
// ...
}
---
原因
getStaticPaths() はビルド時に別のスコープで実行されるため、フロントマターで定義した定数にアクセスできない。
解決策
パターン1: getStaticPaths() 内で定義
---
export async function getStaticPaths() {
const ITEMS_PER_PAGE = 10; // ✓ ローカルで定義
// ...
}
---
パターン2: 外部ファイルからインポート
// src/config.ts
export const ITEMS_PER_PAGE = 10;
---
import { ITEMS_PER_PAGE } from '../config';
export async function getStaticPaths() {
const pageSize = ITEMS_PER_PAGE; // ✓ インポートされた定数
// ...
}
---
デバッグのコツ
getStaticPaths() の catch でエラーを握り潰すと、ページが0件生成される(404)という症状になり、原因特定が困難。最低限ログ出力を:
export async function getStaticPaths() {
try {
// ...
} catch (error) {
console.error('getStaticPaths failed:', error); // 必ず出力
return [];
}
} 問題
REST APIとRust CLIを別々に開発すると、レスポンスのJSON構造が噛み合わなくなる。
典型的な不一致パターン:
| API が返す | CLI が期待する | エラー |
|---|---|---|
[{...}] (配列直接) | { "items": [{...}] } (ラップ) | invalid type: map, expected a sequence |
{ "success": true } | { "message": "..." } | missing field 'message' |
{ "id", "name", "extra_field" } | { "id", "name" } (フィールド不足) | missing field 'extra_field' ではなく別のエラー |
対策
- APIレスポンスは常にオブジェクトでラップする(
{ tasks: [...] }形式)。拡張性も高い - Rust側のデシリアライズ構造体は、APIが返す全フィールドを網羅するか、
#[serde(deny_unknown_fields)]を付けない(デフォルトで未知フィールドは無視される) - API仕様を先に決めてから両方実装する。OpenAPI等で契約を明文化するのがベスト
開発中のCLI実行方法
ビルドしたバイナリはプロジェクト内に生成されるため、PATHが通っていない。 実行するには毎回フルパスを指定するか、手動でPATHに通す必要がある。
# 方法1: シンボリックリンクでPATHの通った場所に配置
ln -s $(pwd)/target/release/my-cli /usr/local/bin/my-cli
# 方法2: cargo install(~/.cargo/bin/ に配置される)
cargo install --path .
リリース後の配布
ユーザーはパッケージマネージャ経由でインストールする。内部的にやっていることは同じ(PATHが通った場所にバイナリを置く)。
cargo install my-cli # crates.io
brew install my-cli # Homebrew
npm install -g my-cli # npm (Node製CLIの場合)
ポイント
npm i -gやcargo installが「魔法」に見えるが、本質は PATHの通ったディレクトリにバイナリを配置する だけ- 開発中は手動で同じことをしているに過ぎない
ln -sはショートカット作成。実体はビルドディレクトリにあるまま
気づき
同一プロジェクトでコンパイル言語(Rust CLI)とスクリプト言語(TypeScript API)を併用していると、修正の反映タイミングが異なることを忘れがち。
| 言語 | 反映方法 | 理由 |
|---|---|---|
| TypeScript (dev server) | 自動反映(ホットリロード) | devサーバーがファイル変更を検知して再起動 |
| Rust (CLI) | 手動リビルド必須 | コンパイル言語なのでバイナリを再生成する必要がある |
実用的な注意点
- API側だけの修正ならリビルド不要で即確認できる
- CLI側の修正は
cargo build --releaseが必要 - 両方修正した場合、API側は即反映されるがCLI側のエラーが残って混乱しやすい
1. ポジショニング: Obsidianとの差別化
Obsidianとは競合ではなくパイプラインの下流に位置づける。
Obsidian = 「書く・繋げる・考える」個人知識管理(インプット〜思考整理)
dev-memo = 「書いたものをタグ集約でWeb公開し、学習を可視化する」出力チャネル
dev-memoだけの価値: Web公開がゼロ設定(git push → 自動デプロイ)/ Learning Heatmap / 記事化候補の自動検出 / noindex限定公開 + OGP + RSS
Obsidianだけの価値: 双方向リンク・グラフビュー / 2000+プラグイン / ローカルファースト
理想フロー:
Obsidianで書く → 公開フラグ付きmdをdev-memoリポに配置 → git push → Web公開 + Heatmap
2. 戦略選択: SaaS vs OSS
王道SaaSが微妙な理由
- Zenn/Qiita/はてなと正面衝突する
- SSR化・認証・マルチテナントDBで技術的複雑度が3倍以上
- 運営コスト(サーバー・サポート・セキュリティ・法務)が個人開発者に重い
- 「サーバーレス」「データはユーザーのGitHub」が最大の差別化なのにSaaSにすると捨てることになる
- 静的サイトの強み(シンプル・安い・速い)を捨てる判断は慎重に
最適解: GitHub Actions + CLIツール + OSSテンプレート
| 方向性 | 実装コスト | 差別化 | ユーザー獲得 |
|---|---|---|---|
| 王道SaaS | 高 | 低 | 低 |
| OSSテンプレート | 低 | 高 | 中 |
| CLIツール | 中 | 高 | 中 |
| GitHub Actions | 低 | 高 | 高 |
GitHub Actionsでゼロ設定デプロイ + CLIで摩擦ゼロ(devmemo new "title" → 編集 → devmemo publish)。SaaSのサーバー運用なしにSaaS的体験を提供でき、OSSテンプレートとして公開すればデータロックインもゼロ。
3. SaaS化の判断基準
「SaaS化」と「マルチユーザー化」は別の判断。まずOSSテンプレートとして公開し、需要を検証する。
- 実装コスト: 数日(README + テンプレートリポジトリ化)vs 数週間(SaaS基盤)
- 検証精度: 「実際にセットアップして使った人数」は社交辞令を排除できる
- 撤退コスト: ゼロ(OSSなので運用義務なし)
SaaS化のGo条件
- GitHub Star 100+ or 月間アクティブユーザー 50+
- 「有料でも使いたい」の声 10件以上
- 「セルフホストが面倒」という要望が複数
SaaS化する場合の唯一の差別化は**「学習活動の対外的な可視化・証明」**。エディタや知識管理でObsidianと戦うのは不毛。
原則
「作りたいから作る」ではなく「ニーズがあるから作る」。
データの保持期間
ヒートマップはGA(Google Analytics)のような期間制限がない。表示用データは無期限保存。
| データ種別 | 保持期間 | 用途 |
|---|---|---|
| 日次集計データ | 無期限 | 圧縮済み集計データ(表示用) |
| 月次集計データ | 無期限 | PV/CV月次合計 |
| 生ログ | 2ヶ月 | 集計前の生データ(定期削除) |
UI上の日付選択範囲
- 通常表示: 固定の最小日付〜現在まで選択可能(ハードコード)
- 自動分析機能: カスタム日付時は過去1年まで
ポイント
- 生ログは毎時バッチで2ヶ月前のデータを削除
- 集計済みデータには削除ロジックがないため、Firestoreに蓄積され続ける
- 過去データの参照制限を設けたい場合はアプリ層で別途制御が必要
概要
frontend-design@claude-code-plugins はAnthropic公式のClaude Codeプラグイン。AIが生成しがちな汎用的なUI(AIスロップ)を防ぎ、洗練されたフロントエンドコードを出力させるスキル。
主な効果
- 大胆な配色・タイポグラフィの選択
- インパクトのあるアニメーション
- コンテキストに応じたビジュアルディテール
- デフォルトスタイルではなく意図的なデザイン
インストール
# Claude Code内で /plugin → マーケットプレイス → anthropics/claude-code → frontend-design
インストール後は「ダッシュボード作って」等の指示で自動適用される。
参考
Cloudflare Pages アプリのアクセス制限
プライベート用アプリにアクセス制限をかける3つの方法:
| 方法 | コスト | 特徴 |
|---|---|---|
| Cloudflare Access (Zero Trust) | 無料50ユーザー | GCP IAP相当。コード変更不要 |
| Basic認証(Honoミドルウェア) | 無料 | UXが悪い |
| OAuth許可リスト | 無料 | 既存OAuth活用 |
Cloudflare Access が最適な理由
- コード変更ゼロ: ダッシュボード設定のみで前段にログインゲートが入る
- 既存のアプリ内認証(Google OAuthなど)と共存可能
- メール、Google、GitHubなど複数のIdPに対応
- デフォルトでワンタイムPIN(メール認証)が使える
設定の流れ
Zero Trust Dashboard > Access > Applications > Self-hosted
→ ドメイン指定 → Policy で許可メールアドレス設定 → 完了
GCP IAP的なことをやりたい場合、Cloudflareならこれ一択。
核心
AI判定の精度向上を考えるとき、「入力データを増やす」「モデルを変える」だけでなく、プロンプト構造を変えるというコストゼロの選択肢がある。
具体的な手法: コンフルエンス分析 + 確信度スコア
複数ルールの補強/矛盾関係をプロンプトに定義し、AIに**確信度(0-100)**を出力させる。
補強関係(同方向) → 確信度UP (+15)
矛盾関係(逆方向) → 確信度DOWN (-20)、見送り推奨
これにより「精度を上げる」と「外れても損しにくい」の両面をカバー。
ラテラルシンキングの5つの視点転換
| 視点 | 問い |
|---|---|
| 入力を変える | データ以外の判断材料は? |
| 判定者を変える | 1つのAIだけが最善か? |
| 時間軸を変える | 毎回ゼロから判定する必要があるか? |
| 出力を変える | 二択/三択が最善か? |
| 否定する | 精度以外に勝つ方法は? |
学び
プロンプト改善は「文言を磨く」ではなく「構造を変える」が最もインパクトが大きい。
概要
Claude CodeのTask toolで4つの思考スタイル(性格)のエージェントを並列起動し、設計判断の質を高める手法。役割ベース(フロント担当・バック担当)ではなく、認知的多様性で盲点を減らす。
4つの性格
| 性格 | 問い | 視点 |
|---|---|---|
| Pragmatist | 最短経路は? | 実用性・コスト・速度 |
| Skeptic | 何が壊れる? | リスク・前提・失敗モード |
| Idealist | 半年後も誇れる? | 品質・拡張性・長期ビジョン |
| Connector | 既存との整合は? | パターン認識・文脈・一貫性 |
導入のポイント
- 全タスクに使わない。重要な設計判断(DB選定、認証方式、SSR/SPA等)の時だけ
--quickモード(Pragmatist + Skepticの2体)でコスト削減可能- 理論的背景: デボノの Six Thinking Hats、アンサンブル学習の多様性原理
- 認知的多様性は条件次第で逆効果(統合基準が曖昧だと矛盾が増えるだけ)
実装
/design-review スキルとして構築。ファシリテーターが4体を並列起動→合意点・対立点・鋭い問いを統合して提示。
/plan → /design-review(技術判断が要る時だけ)→ /tdd-workflow