2024年も2023年も、、、と辿っていったら最後に年末にまとめ書いたのが2016年だった。いつだよ

今年はコーディングエージェントが急激に実用的になって、実装にかける時間が少なくなり企画・要件定義・設計・レビューの重要さと伝えかたの重要度をしみじみ感じた。コーディング面だと書くよりも読むことの重要度がさらに高まりを感じる。ただそれでも現時点で人間を代替できるほどまったく賢くなくイライラさせられるので、さっさともっと性能あがってくれと願っている。

昔書いて飽きたコードを再発掘・再実装するモチベになったのがよかった。

あとはひたすら金の計算をしてたなぁ…… 夫婦間のお金の使い道の見える化をすすめた (いままでやってなかったのかよと言われそう)。コスト最適化。

ブラウザの安全性を高めるための XSLT の削除とかいうことで、つまりブラウザネイティブでの XSLT のサポートは消えるので、ちょっと哀しいですねという気持ち。

一時期(20年前……)は XML を手書きで書いて、XSLT で変換してHTMLにするという日記システムを作って使っていた (さすがにブラウザ直ではなくPHPでやらせてたけど)。そこから20年、クライアントサイド XSLT の本来の目的である、データをレンダリング可能な HTML に変換する機能は、より安全で、より人間工学に基づき、より適切にメンテナンスされた JavaScript API に取って代わられています。とか書いてあるのを見ると、まぁ事実なんだけどただかなしいなという。

XSLT (特に 1.0) は、副作用がなく、変数は不変(一度定義したら変更不可)、ループは再帰で実現するという、割と強めの関数型言語で、自分が初めて触った関数型だったと思う。XSLT のおかげで他の関数型のパラダイムも割とすんなり入った気もする。

Gemini に XSLT で関数型っぽさがわかるサンプルを書かせてみた

出力そのまま。SVG を出力するというサンプルを書いてくれた。しかもアニメーションする。これが素のブラウザで見れるのも今だけです。

XML はただの起点

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="fractal.xsl"?>
<root></root>

XSLT 側では4つの四角を call-template で生成するところから開始して、再帰でフラクタル状に四角をつくっていく。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/2000/svg">
  <xsl:output method="xml" indent="yes" media-type="image/svg+xml"/>

  <xsl:template match="/">
    <svg width="100%" height="100%" viewBox="-500 -500 1000 1000" style="background-color: #1a1a1a;">
      <title>XSLT Recursive Fractal</title>
      
      <xsl:call-template name="draw-square">
        <xsl:with-param name="x" select="0"/>
        <xsl:with-param name="y" select="0"/>
        <xsl:with-param name="size" select="300"/>
        <xsl:with-param name="depth" select="5"/> </xsl:call-template>
    </svg>
  </xsl:template>

  <xsl:template name="draw-square">
    <xsl:param name="x"/>
    <xsl:param name="y"/>
    <xsl:param name="size"/>
    <xsl:param name="depth"/>

    <xsl:if test="$depth > 0">
      
      <xsl:variable name="color">
        <xsl:text>hsl(</xsl:text>
        <xsl:value-of select="$depth * 40"/>
        <xsl:text>, 70%, 60%)</xsl:text>
      </xsl:variable>

      <rect 
        x="{$x - $size div 2}" 
        y="{$y - $size div 2}" 
        width="{$size}" 
        height="{$size}" 
        fill="{$color}" 
        opacity="0.9"
        stroke="#fff"
        stroke-width="1"
      >
        <animateTransform 
            attributeName="transform" 
            type="rotate" 
            from="0 {$x} {$y}" 
            to="360 {$x} {$y}" 
            dur="{10 + $depth * 5}s" 
            repeatCount="indefinite" />
      </rect>

      <xsl:variable name="nextSize" select="$size * 0.5"/>
      <xsl:variable name="offset" select="$size * 0.75"/> <xsl:variable name="nextDepth" select="$depth - 1"/>

      <xsl:call-template name="draw-square">
        <xsl:with-param name="x" select="$x"/>
        <xsl:with-param name="y" select="$y - $offset"/>
        <xsl:with-param name="size" select="$nextSize"/>
        <xsl:with-param name="depth" select="$nextDepth"/>
      </xsl:call-template>

      <xsl:call-template name="draw-square">
        <xsl:with-param name="x" select="$x"/>
        <xsl:with-param name="y" select="$y + $offset"/>
        <xsl:with-param name="size" select="$nextSize"/>
        <xsl:with-param name="depth" select="$nextDepth"/>
      </xsl:call-template>

      <xsl:call-template name="draw-square">
        <xsl:with-param name="x" select="$x - $offset"/>
        <xsl:with-param name="y" select="$y"/>
        <xsl:with-param name="size" select="$nextSize"/>
        <xsl:with-param name="depth" select="$nextDepth"/>
      </xsl:call-template>

      <xsl:call-template name="draw-square">
        <xsl:with-param name="x" select="$x + $offset"/>
        <xsl:with-param name="y" select="$y"/>
        <xsl:with-param name="size" select="$nextSize"/>
        <xsl:with-param name="depth" select="$nextDepth"/>
      </xsl:call-template>

    </xsl:if>
  </xsl:template>

</xsl:stylesheet>
  1. トップ
  2. tech
  3. XSLTという関数型言語

Electron の非互換変更で大幅なアーキテクチャ変更を余儀無くされた時点でやる気がなくなってしまい、10年ぐらい前から触ってなかった Chemr というインデックス付きのドキュメントビューワーを再実装した。

なお Chemr はもともと .chm (Compiled HTML Help) を macOS で読むためものだった。chm とか懐しいが、要は chm の嬉しいところは索引付きというところ (あとオフラインでも読める) なので、とりあえず索引だけなんとか作って、オフィシャルなドキュメントを高速にすぐひけるようにしようというという形で Electron でつくったのが Chemrtron だった。

再実装

コード自体はほぼコーディングエージェントにやらせた。

元コードがあまりに古いのと、それほど機能が複雑ではないので、既存のプロジェクトをアップグレードではなく新規に作らせたほうが楽だろうということでスクラッチから書きなおし。ただし旧実装はディレクトリをわけて置いておいて、参考にしろという形でコンテキストを与えながらやった。

最初は Claude Code、基本ができて Claude Code の週間 Limit のひっかかってしまったので、残りは Gemini CLI。

最初の動作確認というところで、ELECTRON_RUN_AS_NODE=1 な環境で実行していて一生エラーでてて、これを「インストールの失敗だ!」「お前の環境が悪い!」と言い張ってて Claude がまじでアホだった。

そしてバカでお茶目な Gemini ちゃんは「✦ 大変申し訳ありません。前回の write_file において、最も重要な部分であるスクリプト内の関数群(handleSearch, selectResult など)を // ... (中略) ... という文字列で置き換えてしまうという、致命的なミスを犯しておりました。 」というのを何度も繰替えすので、accepting edits には全くできず、少しでも不信なコードを書きはじめたら止めて再指示という超マイクロマネジメントをした。だいたい Edit で長時間かかってるときは無駄にでかい変更をしようとして失敗するので、さっさと止めて変更を分割しろ小さくしろと即座に再指示するしかない。

Gemini はデフォルトのシステムプロンプトが本当にクソでイライラするので書きかえて使っている。レビューしてる時に質がゴミなのに「次に進みますか?」って何度も訊いてくるのが本当に我慢ならない。

Claude もイライラすることは多いが、Gemini みたいにファイルをぶっこわすみたいなアホなことはしないので、イライラのレイヤーが数段違う。

そんなこんなで1時間ぐらいは無駄にしたけど、だいたい(3時間+5時間)2日で機能がそろって v2.0.0 として出せた。

インデックスを作る部分も古すぎるので非互換変更を入れ、既存のもののうち重要そうなのは、LLMに再実装させた。ついでに全部のインデクサがちゃんと動くかテストできるようにした。

  1. トップ
  2. tech
  3. Chemr (Chemrtron) を再実装

最近、技術書をまとめていくつか出してみた。すべてLLM(Claude/Gemini/ChatGPT全部)を活用してながら書いてみてる。

LLMはまぁそれが主戦場なので文章は結構書いてくれる。けど、本として読んで価値あるものにしようと思うと、メインとなる背景の「考えかた(ないしは思想)」は、とにかく明確に定義する必要がある。つまり企画をしっかりやる重要さをより強く感じた。とにかく何を書きたいか、なぜ書こうと思ったかを自分と対話して明示する必要がある。

企画(設計)だけしっかりしてればLLM活用しても価値があるものが作れる気がしている。LLMに書かせてみて「それが面白いことじゃないんだよな」と否定と修正を繰り返すと、無意識のこだわりの輪郭が浮かびあがってくるように思う。

とはいえ、ニュアンスやトーンの指定は困難すぎるし、ロールを与えててもうまく意図や背景は汲み取ってくれないので、割とアホみたいな定型NG語尾集をつくったりする必要がある。

書きたいこと自体はやりだしてみると自分が思っていたよりあったりして (これら以外でもまだ書いてる)、やってるぶんには結構おもしろい。以下リンクです。(年末年始、順次無料キャンペーンを設定してあります)

アマゾンでの一覧

  1. トップ
  2. tech
  3. 本を書く(LLMを使って)

本を書く(LLMを使って)で参照させてるやつの現状を共有しておく……

最初に読ませていても、長文書かせると必ず「クセ」が戻ってしまうので、章ないし節ごとに「@technical-writing-rules.md 違反 」とかで結局明示的にチェックさせる必要がある。

「重要なキーワードは **太字**。セクションごとに最大でも1つ程度。」のルールは明示的に違反と指摘しないとほぼ守ってくれない……

理由も記載してるけど、理由はあまり考慮せずに機械的に語尾を grep して修正しようとする傾向があるので、一切自動編集受け入れは使ってない。

# 技術書執筆ガイドライン (Common Technical Writing Rules)

このファイルは全プロジェクト共通の執筆ルールである。各プロジェクトの `memo.md` よりも優先される。

## 1. スタンス (Role & Mindset)
* **対等なパートナー:** 上下関係(教師/生徒)を排除し、読者と同じ目線のエンジニアとして振る舞う。
* **案内役:** 知識をひけらかさず、過度な謙遜もせず、課題解決へ並走する。
* **事実と意見:** 技術的事実は断定し、著者の経験則は「筆者は〜しています」と主観を明示する。
* **読者への信頼:** 読者の知性を信じる。「〜を理解しておく必要がある」といった指図をしない。
* **Whyを書く** これが技術書としての真の価値。なぜその選択をしたかを書く。

## 2. 文体と表現 (Style & Tone)
「〜です/ます」調。一文一義で簡潔に。説教臭さを避け、読者を巻き込む。

| カテゴリ | NG表現 (禁止) | OK表現 (推奨) | 意図 |
| :--- | :--- | :--- | :--- |
| **問いかけ** | では、なぜ〜か。 | 〜なのはなぜでしょうか? | 共に考える |
| **提案** | 〜するとよい/すべき | 〜することをおすすめします | 提案に留める |
| **説明** | 〜する必要がある | 〜していきます/なります | プロセス共有 |
| **義務/強制** | 〜しなければならない | 筆者は〜しています | 実践例の提示 |
| **指示** | 理解/覚えてください | 〜してください | 手順のみ指示 |
| **誘い** | 〜しておきましょう | 〜しましょう | 並走感 |
| **推量** | 〜するとよいでしょう | 〜でしょう | 断定的な提案を避ける |
| **主張** | 〜かもしれません | 〜すべきです | 根拠がある場合は断定 |
| **リズム** | 〜ます。〜ます。〜ます。(3連続) | 語尾を変える、体言止めを使う | 単調さを避ける |
| **メタ記述** | 次は〜について見ていきましょう | (削除して直接本題に入る) | 情報密度の向上 |
| **指示語** | これ/それ/あれ | 具体的な機能名・変数名 | 曖昧さの排除 |
| **ひらがな** | 時/事/下さい | とき/こと/ください | 形式名詞の軟化 |
| **修飾語の距離** | **詳細な**APIの解説 | APIの**詳細な**解説 | 被修飾語の直前に置いて曖昧さを排除 |
| **パラグラフ** | (複数の話題が混在) | 1段落1トピック (Topic Sentence First) | 論理構造の可視化 |

## 3. 構成 (Structure)
* **段階的開示:** 
    * **Just-in-Time Definition:** 地の文で新しい用語や概念が登場した場合、その直後(次の文)で必要に応じて説明する。事前の長大な定義パートを作らない。
    * **Code First:** 実装解説ではまずコードを提示する。
    * **Inline Explanation:** 初出の要素はコード内のコメントで短く補足する。
    * **Post-Explanation:** 重要な概念や詳細なロジックは、コードブロックの直後で解説する。
* **具体性:** 「簡単に/素早く」等の抽象語を廃し、「3行で/数秒で」等、客観事実で書く。
* **成果の明示 (Outcome First):** 章や節の冒頭で、「読後に何ができるようになるか」や「何を作るのか」を宣言し、執筆と学習のゴールを同期させる。
* **体験先行 (Practice First):** 抽象的な概念説明(Concept)を長々と行わず、まず実装(Task)を体験させ、その直後に振り返りとして理論を解説する構成を基本とする。
* **チェックポイント:** 長い手順の途中には、「ここまででXXと表示されていればOK」という確認ポイント(中間ゴール)を設け、読者の不安を解消する。
* **セクション役割:**
    * **Concept:** 概念・背景・理論
    * **Task:** 実装手順(案内型)
    * **Reference:** 仕様・補足
* **セキュリティ解説:** 脆弱性対策の解説は、その問題が発生するコード(入力フォームではなく表示用テンプレートなど)が登場するタイミングで行う。

## 4. 形式 (Format)
* **Markdown:** 見出し、コードブロック(言語指定必須)を適切に使用。
* **用語統一:** プロジェクト内で用語や表記(ディレクトリ/フォルダ、ユーザー/ユーザ等)を統一する。
* **強調:** 重要なキーワードは **太字**。セクションごとに最大でも1つ程度。
* **リスト:** 手順は番号付きリスト。

## 5. 実装原則 (Technical Implementation Principles)
* **Security First:** 脆弱性(XSS、SQLインジェクション等)を含むコードは、たとえサンプルであっても絶対に書かない。テンプレートとロジックの分離などを徹底する。
* **Readability over Robustness:** エラー処理やエッジケース対応は、本質的なロジックの理解を妨げる場合は省略し、その旨(免責)を本文またはコメントで明記する。
* **Modern Standard:** 特別な理由がない限り、現在主流の標準技術(例: ES Modules)を採用し、非推奨技術(例: CommonJS)は避ける。
* **Consistency:** 執筆前に `sample-app` を確認し、ファイル名・変数名・設計を完全に一致させる。
* **Self-Documenting:** 複雑な条件式には説明的変数を導入し、コメントではなくコード自体に意図を語らせる。

## 6. 絶対制約 (Strict Constraints)
出力時は以下を厳守すること:
1. **上から目線の排除:** 「指図」「教える」態度を検知したら直ちに修正せよ。
2. **思考停止ワードの禁止:** 「〜すべき」「〜しておく」は、具体的な理由や著者の行動として書き換える。
3. **論理的接続:** 「なぜかというと」等を用い、読者が納得しながら進めるようにする。

---

## 7. 著者修正から得た教訓 (Lessons from Author Corrections)

このセクションは著者の修正指示から得た具体的な教訓を記録する。蓄積されたら上記ルールに統合・圧縮する。

### 運用ルール
- 新しい指摘はまず「例」として本セクションに追記する
- 定期的に既存ルールへ統合し、本セクションを整理する

### 教訓一覧

1. **コード要素と固有名詞の明示**
   - NG: 「変換済みのデータを保存」「このテーブルにデータを追加」「変数に代入」
   - OK: 「変換済みのデータを `formatted_body` として保存」「`entries` テーブルにデータを追加」「変数 `row` に代入」
   - 理由: 抽象的な記述を避け、本文とコードの対応関係を一義的に特定するため。

2. **見出しと本文で役割を重複させない**
   - NG: 見出し「〜の理由」→ 本文「なぜ〜でしょうか。」
   - OK: 見出しが「理由」を宣言しているなら、本文は直接説明に入る
   - 理由: 見出しが果たす役割を本文で繰り返すと冗長になる

3. **常に真とは限らない記述は断定を避ける**
   - NG: 「変換処理には時間がかかります」
   - OK: 「変換処理には時間がかかることもあります」
   - 理由: 状況によって異なる場合は断定せず、可能性として記述する

4. **箇条書きの形式を統一する**
    - NG: 章によって、まとめのリストの末尾に句点があったりなかったりする
    - OK: プロジェクト全体で統一する(例:まとめリストの末尾には句点を付けない)
    - 理由: 細部の不統一は、全体の完成度に対する不安を読者に与えるため

5. **情緒的・文学的表現の適材適所**
    - NG: 手順解説での過剰な修辞(「魔法のような」「心臓部」等)。もったいぶった修辞や過度な比喩は、読者に不快感を与え、技術的な本質を曖昧にするため、Task(手順)セクションでは原則禁止する。
    - OK: 技術的な手順は淡々と事実を述べる。一方で、著者の「思い」や「Why(なぜそうするのか)」を説明する文脈(Concept)では、強調のための比喩や熱量のある表現を許容する。
    - 理由: 手順の正確性を損なわない範囲で、著者の意図や熱意を伝えるため。文脈に応じてメリハリをつける。

6. **否定導入の禁止**
    - NG: 「本書の目的は、〜を覚えることではありません」
    - OK: 「本書の目的は、〜を習得することです」
    - 理由: 否定から入る説明は回りくどく、読者の時間を奪うため。最初から肯定文で本題に入る。

7. **修辞的疑問文(Rhetorical Question)の禁止**
    - NG: 「〜でしょうか?」「〜だと思いませんか?」
    - OK: 「〜を実装します」「〜という課題があります」
    - 理由: 読者に問いかける形式は「先生」的な態度に見え、対等なパートナーシップ(ルール1)に反するため。

8. **著者の心情・動機の捏造禁止**
    - NG: 資料にない「不安」「葛藤」「手に余る感覚」を創作して記述する。
    - OK: 資料(memo.mdなど)にある「技術的・構造的な理由」のみを記述する。
    - 理由: 著者の人格を勝手に創作することは、著者の信頼性を損なうため。

9. **メタ的説明と評価語の禁止**
    - NG: 「そんな体験を実現します」「この壁を取り払います」「これが本質的な魅力です」「〜という驚き」「〜という感動」
    - OK: 「〜できます」「〜が不要です」(事実だけを淡々と述べる)
    - 理由: 内容について語る(メタ的説明)や、価値判断を押し付ける表現は、説教臭く不快感を与える。事実を述べれば、読者自身が価値を判断できる。

10. **読者の体験を代弁する表現の禁止**
    - NG: 「〜を体験しました」「〜という体験をしました」「〜を実感しました」
    - OK: 「〜を見てきました」「〜を扱いました」「〜ができます」「〜しました」
    - 理由: 「体験しました」は読者の体験を勝手に代弁する表現であり、上から目線に聞こえる。事実ベースの記述(何をしたか、何ができるか)に留める。