技術書の執筆を支える技術

IDチームの小森です。 CADDi プロダクトチーム Advent Calendar 2024の18日目の記事になります。

いよいよ年の瀬ですが、皆さんは年初に立てた目標などを達成できましたでしょうか。 私はずぼらなので年初の目標など立てたことはないのですが、今年ようやく5年越しの取り組みに一区切りをつけ、誓いを果たすことができました。 私事ですが、人生3冊目の著書『[改訂新版] プロになるためのWeb技術入門』を2024年11月28日に刊行しました。 おかげさまで好評をいただいております。

本エントリーでは、技術書執筆の技術的な側面について、私が取り組んできたことを紹介したいと思います。

技術書執筆に用いるマークアップ言語

技術系の文書は何らかのマークアップ言語で作成することが多いでしょう。 おそらく、以下のあたりが選択肢になると思います。

今回私は AsciiDocAntoraを採用しました。 Antoraについては、馴染みがない方も多いと思うので、後ほど軽く紹介します。

一般的に出版社を通した技術書の商業出版では、紙面のデザインは出版社側にお願いすることになるので、著者が細かい所まで作成する必要はありません。 どのような形式で出版社に原稿をお渡しするかは、出版社との取り決め次第です。

私の場合は、自分でもある程度紙面をイメージしながら執筆したいので、前著まではMicrosoft Wordで執筆し、Wordファイルを出版社に提出していました。 今回、AsciiDocを選んだ理由はこのあと述べますが、Antoraという静的サイトジェネレーターで綺麗にHTML化できることも理由の1つでした。

大規模技術文書に必要な要件

今回執筆した書籍は紙にすると528ページと、技術書の中でもかなりのボリュームになります。

経験者の方も多いと思いますが、一定規模以上の技術系文章を執筆する場合、Markdownでは厳しい面が多いです。 私個人としては、以下のような機能は必須と考えています。

  • 見出し、図表の自動ナンバリング
  • 相互参照
  • ソースコードのinclude
  • 複雑な表組み (セル結合など)
  • 注釈
  • 参考文献

昨年は当社メンバーでSoftwareDesign誌に連載を寄稿させていただきましたが、その際はハードルの低さを重視してMarkdownで執筆しました。 1回あたり6〜8ページの分量でしたが、個人的にはMarkdownで書けるのはこのくらいの分量が限界かなと思っています。

上に挙げたような機能は特別なものではなく、昔から組版の世界で使われているTeXではあたり前に備わっているものです*1

本書の執筆では、初期段階でたまたま試しに使ってみたAsciiDocが肌に合ったので、そのまま採用することにしました。 AsciiDocは、Markdownとは細かい文法に若干の違いがあるものの、概ね同じ感覚で記述することができ、上に挙げたような機能も備わっています。 特に重視したのは、AsciiDocベースの静的サイトジェネレータであるAntoraの存在です。

Antoraの採用

Antoraは、AsciiDocで記述したドキュメントから静的サイトを構築するツールです。 主に、ソフトウェアプロダクトのドキュメントサイト構築用途だと思いますが、技術書の執筆にも十分利用できました。 私にとって特に役に立ったのは、以下のような特徴です。

  • 複数のソースファイルに分割された大規模ドキュメントからなる静的サイトを生成できる
  • サイドバーに目次を表示できる
  • Gitのタグやブランチ毎に複数バージョンのサイトを生成して切り替えられる

Antoraのドキュメント自体がAntoraで出力されていますので、出力イメージはこちらを見ると良いと思います。

好みの問題かもしれませんが、私が技術書の原稿を書くときはできるだけ紙面に近いイメージを頻繁にチェックしながら作業します。 前著までMicrosoft Wordを使用していたのもこのためでした*2。 もちろん、執筆のモチベーションが維持しやすいという理由もあります。

Antoraでは、HTMLテンプレートやCSSのカスタマイズも可能なので、細かな調整をして紙面のイメージに近づけています*3。 前述のように、実際の紙面デザインは出版社側のプロの方が行うので、著者が細かくこだわる必要はありません。 しかし、イメージを伝える手段としては有用かなと思っています。

Antoraによる執筆

Antoraによる出力の実際

Antoraでは、本文を記述するAsciiDocファイルをはじめ、いくつかの設定ファイルや画像、サンプルコードなどを、決められたディレクトリ構成で配置しておきます。 以下は、簡略化したディレクトリ構成のイメージです。 ここでは1章分を1つのAsciiDocファイルとして表現していますが、実際はincludeができるので、コラムなどを別のファイルに分割するなどしています。

|-- antora-playbook.yml (👈サイト構築構築方法の記述ファイル)
|-- antora.yml (👈Antoraの設定ファイル)
`-- modules
    `-- ROOT
        |-- examples
        |   `-- サンプルコード
        |-- images
        |   `-- 画像
        |-- nav.adoc (👈左サイドバーのナビゲーション)
        `-- pages
            |-- chapter-01.adoc (👈原稿本体)
            |-- chapter-02.adoc
            |-- ・・・

AntoraはDockerイメージ版が用意されているので、このディレクトリ上で次のように実行するだけで、サイトが構築できます。

docker run -u ${UID} -v .:/antora:Z --rm -t antora/antora:3.1.9 antora-playbook.yml

執筆中はローカルPC上でサイト生成して確認し、出版社の方や査読者に確認してもらうときは、あとで説明するGitLab pages経由でこのサイトを共有することで、他の人にも同じイメージを確認してもらえます。

Antoraで生成された原稿
Antoraで生成された原稿

複数バージョンドキュメントの生成

AntoraはGitとも連携しており、Git上の任意のタグやブランチ単位で複数バージョンのサイトを生成し、切り替えて閲覧することができます。 よくある利用例は、オープンソースプロダクトのドキュメントで「バージョン1.x系」と「バージョン2.x系」のドキュメントを切り替えて見られるようにしたものです。

本執筆では、Asciidocで書いた原稿をGitリポジトリ上で管理し、CIによって自動でAntoraでHTML化しつつ、編集の方や査読者にみてもらうものはGit上に打ったTag毎のバージョンで公開するのに活用しました。 こうすることで、ある時点のスナップショットを他の人にチェックしてもらうのが容易になります。

以下のように、画面左下からバージョンを選択できるようになっていて、常に最新版のmainブランチの原稿とrc-2025-06-26のように特定タグでの原稿を切り替えられます。 査読者にとっては、査読中に原稿がアップデートされると混乱するため、タグの方で見てもらうようにしました。

バージョン毎のドキュメント

Antoraによるサイトジェネレート

ローカル実行

Antoraによるサイトの構築はコマンド一発で実行できる*4とはいえ、頻繁に実行するとなると執筆のペースが乱れます。 そこでフロントエンド開発と同じような感覚で、原稿ファイルを保存したらAntoraを自動実行して生成結果をすぐに確認できるようにしたいと思い、fswatchコマンドで原稿ファイル(*.adoc)の変更を検出し、Antoraを実行するシェルスクリプトを書きました。

このような処理はWebpack等のツールを導入すれば実現できますが、ツールチェインを増やしたくなかったこととそれほど難しくなかったので、自作しています。

また、Macではosascriptコマンドでデスクトップ通知ができます。 原稿が増えるとAntoraの処理も数十秒かかるようになるため、以下のような処理をビルドスクリプトに組み込んで、生成完了を通知するようにしていました。

/usr/bin/osascript -e "display notification \"BUILD SCUCESS\" with title \"build.sh\""

これだけの工夫で執筆体験はかなり向上しました*5

GitLab pagesでの公開

筆者は自宅サーバでGitLabをセルフホストして運用*6しており、原稿もそこで管理しています。 GitLabにも、GitLabCI というGitHub Actions相当のCI/CDをサポートする機能があるので、これを利用しました。

原稿をGitLabにpushしたら、前述のビルドスクリプトが実行され、GitLab PagesにHTMLが公開されるところまでを自動化しています。 また、原稿を出版社の方や査読者に見せるときは、GitLab上でタグを作るだけでGitLabCIが走り、そのタグのバージョンのHTMLが生成されます。 あとは、このURLを伝えて原稿をチェックしてもらいます。昔はWordの原稿をメールに添付して送っていたので、ずいぶん楽になりました。

draw.ioによる作図

本書では図とスクリーンキャプチャが250点近くあります。 技術書とはいえ、文字だけが書かれたページを読むのは読者が疲れると思うので、なるべくページのどこかに図表・ソースコード等が1点は含まれるようなバランスにして、紙面に変化が出るように心掛けています。 特に図は重要で、文章での説明を図でも視覚的にも表現することで、理解のしやすさが大きく向上すると考えています。

これらの図は、すべてデスクトップアプリ版のdraw.ioで作図しています。 draw.ioで作成した図をdrawio.svg形式でローカルファイルにエクスポートすると「SVG画像が埋め込まれたdraw.io」の形式となるので、これをAsicciDoc形式のファイルと一緒にGitで管理しておきます。 こうすることで、AsciiDocからdrawio.svgファイルを直接指定してAntoraでの生成時に利用でき、便利です。

実際には、これを元にデザイナーさんが綺麗な絵を書き起こしてくれるのですが、技術的なことを説明する図は原稿の段階でなるべくきちんと作っておいたほうがスムーズです。

技術書におけるサンプルコードの管理

技術書でサンプルコードを原稿にどう取りこむかは、意外と面倒なテーマです。 特に本書では、紙面で紹介しているサンプルコードを実際にGoogle Cloudでホストして読者が動作確認できるようにしています。

以前、Wordで原稿を書いていたときは、別管理しているサンプルコードから必要な箇所を原稿にコピー&ペーストしていました。 最初は良いのですが、サンプルコードを修正したときの反映が非常に手間で、ミスもたびたび発生します。

Antoraはテクニカルライティング向けのツールということもあり、サンプルコードの取り込みに関する機能も充実しています。

今回は、Antoraの標準ディレクトリ構成に則って modules/ROOT/examples 配下に個々のサンプルアプリケーションのディレクトリを作成し、そこで原稿と一緒にサンプルコードを管理しました。 概ね、以下のような構成です。

`-- modules
    `-- ROOT
        |-- examples
        |   |-- example-01  (👈サンプルアプリ1)
        |   |  `-- main.go
        |   |-- example-02  (👈サンプルアプリ2)
        |   |  `-- main.go
        |   ・・・
        `-- pages
            |-- chapter-01.adoc (👈原稿本体)
            |-- chapter-02.adoc
            |-- ・・・

AsciiDoc内でサンプルコードを参照するときは、以下のように記述することでインクルードできます。 これで、サンプルコードを修正したときも原稿に自動反映できます。

[source,go]
----
include::example$example-01/main.go[]
----

なお、実際にはソースコードのファイル全体を掲載することはまれで、必要に応じて特定関数など一部分を掲載することの方が多いでしょう。 そのようなときは、ソースコード側に以下のように tag::タグ名[]end::タグ名[] という形式のコメントを入れておきます。

・・・

// tag::new-session[]
// 新しいセッション情報を生成する。
func NewHttpSession(sessionId string, validityTime time.Duration) *HttpSession {
    session := &HttpSession{
        SessionId: sessionId,
        Expires:   time.Now().Add(validityTime),
        PageData:  "",
    }
    return session
}

// end::new-session[]

・・・

AsciiDoc側では、include時に [tag=タグ名] と指定することで、特定箇所だけをincludeすることもできます。

.NewHttpSession関数(session.goより抜粋)
[source#list_tinytodo-user-newhttpsession,go]
----
include::example$06-07_tinytodo-user/session.go[tag=new-session]
----

これによって、実際に公開するコードと紙面に掲載するコードが乖離する問題を防ぐことができました。 特にサンプルコードの多い本書では、この機能に本当に助けられました。

Google Cloudでのサンプルアプリの公開

このように作成したサンプルアプリケーションは、Google Cloudの無料枠*7を使い、Google Cloud上でホストしています。 概ね、以下のようなことをしていますが、これらについては、すでに多くの情報が公開されているので、本エントリでは割愛します。 (Terraform周りは、SoftwareDesign誌の連載『Google Cloudを軸に実践するSREプラクティス』でも紹介しています。本ブログの転載記事か、『Software Design総集編【2018~2023】』をご覧ください!)

  • サンプルコードをGoReleaserでビルドできるようにし、GitLab CI上でビルド
  • ArtifactRegistryの使用容量を抑えるため、すべてのサンプルアプリケーションを1つのDockerイメージにまとめ、起動引数で実行アプリケーションを切り替える仕組みにした
  • アプリケーションはCloudRun serviceとして実行
  • Google Cloudのインフラ構築まわりはTerraformで管理し、GitLab CI上から実行

査読レビューについて

今回は、総勢14名の方に査読を依頼しましたが、フィードバックの受け方は悩みました。

当初は、原稿を管理しているGitLab上でPullRequest*8の形でコメントをもらおうかと思いましたが、以下の理由でやめました。

  • ひととおり書き上がった大量の原稿ファイルをPullRequestの差分として作るのが難しかった
  • AsciiDocのマークアップに対してコメントを書いてもらうのが、レビュアーにとって負担になりそうだった

結局、以下のようにPDFの注釈機能を使ってコメントをもらう方式にしました。

  1. Antoraで生成したサイトをChromeで表示し、PDFへの出力機能でPDF化 *9
  2. PDFをGoogleDriveでレビュアーに共有
  3. レビュアーはGoogleDrive上でPDFを開いて原稿をチェックし、コメントを入れる

このやり方なら、レビュアーがPDFの編集ツールを持っていなくも注釈を入れられるというメリットもあります。 GoogleDrive上では、コメントのやりとりなどのコラボレーション機能もあるため、重宝しました。

課題と反省点

AsciiDoc/Antoraを中心に執筆環境を構築したことは概ね良かったのですが、いくつかの課題と反省点はあります。

AsciiDocの反省点

章を跨いだときの参照が弱い

「2.4. サーバサイドの構成要素」で説明したように〜 と、別の章の見出しを参照したいケースがあります。 Antoraでは、章を跨いで見出しや図表を参照するとき、その見出し文字列の部分を自動で取り出してくれません*10

このため、以下のようにリンクテキストも含めて直接指定せざるを得ないのですが、推敲段階で見出し番号や節タイトルが変わることも多いので、不正確になってしまいます。

被参照側

[#consists-of-serverside]
== サーバサイドの構成要素

(・・・・文章・・・・)

参照側

「xref:02-web_system_overview.adoc#consists-of-serverside[2.4 サーバサイドの構成要素]」で説明したように〜

これについては、AsciiDocを簡易パースして見出しや図表の一覧を作り、クロス参照している箇所の見出しが正しいかをチェックするツールをGoで作ることで対処しました。 脱稿後に少し時間の余裕ができてから作ったのですが、早い段階で作っておけば良かったと思っています。

デザイナーさんに優しくない(たぶん)

書籍の版下は、Adobe社のInDesignなどで作られることが多いようです。 ここから先は私の想像なのですが、InDesignへのテキストの流し込みは手作業になってしまい、AsciiDocに限らずマークアップ記述がこの作業の邪魔になるようです。

たとえば、私の原稿上でURLを紹介する箇所は次のようにマークアップしていました。

[.url]*https://worldwideweb.cern.ch/*

これがデザインされるとき、 https://worldwideweb.cern.ch/* のように、最後のアスタリスクが消し忘れられてしまうといったことがよくありました。 デザイナーさんにとっては https://worldwideweb.cern.ch/ のようなプレーンなテキストのほうが、作業しやすいはずです。 次の機会には、原稿提出用にAsciiDocを加工するツールを作ってみようかとも思っています。

自動校正

textlintRedPenのようなツールを使った自動校正に、興味を持っている方もいるかもしれません。 私も一時期使ってみたのですが、肌に合わずやめてしまいました。

当初は、VSCodeにtextlintのプラグインを入れ、執筆中に表記揺れや言い回しなどを指摘してくれるようにしていました。 しかし、執筆中に指摘が出るとその指摘を潰す方に意識が向いてしまい、かえって集中できないという結果になり、途中でやめました。

著者の執筆スタイルにもよるかもしれませんが、私の場合は推敲・校正段階でなんども読み直して文章を直していくので、読みにくさや言い回しはその段階で大抵直ります。 また、出版社側でのチェック・修正もしてもらえます。 強いて言えばCIでこれらのツールを掛けられれば良かったのですが、素直に出版社に頼ることとして、著者としての仕事に注力することにしました。

とはいえ、自動チェックを併用できるに越したことはありません。 CI上でチェックしてPull Requestを自動作成するといったことができれば、著者・出版社にとって多くのメリットが得られるかもしれません。 これについては、次に挑戦してみたいと思っています。

まとめ

AsciiDocとAntoraを中心に、大規模技術書の執筆時の工夫ポイントを紹介しました。 本来の用途通り、OSSプロダクト等のドキュメントやAPIドキュメントなどの公開にも十分力を発揮できると思います。 また書籍の出版はとても多くの労力が必要ですが、自分の文章が本という形になり書店に並ぶのは、別格の達成感があります。 ぜひ、機会を捕まえてチャレンジしてみてください。

*1:余談ですが、この対極で、TeXはドナルド・クヌース氏が自著の執筆にあたり、組版レベルまで自分で制御したいという必要性から開発されたものです。紙面イメージまで自分で作るなら最強で、学生時代の卒論執筆などではお世話になりましたが、なかなかハードルが高い面があると感じます。

*2:当時は執筆に耐えるマークアップ系の技術はTeXくらいしかなかったというのもありますが

*3:いささか邪道ですが、生のHTMLを組み込むこともできます。書籍のサポートサイト((https://support.webtech.littleforest.jp/)も、Antoraで作ってしまいました

*4:VSCodeではAsciiDocのプラグインを導入することで、ある程度のリアルタイムプレビューはできるのですが、Antoraとは違うので限定的です。

*5:ここではさらっと書きましたが、実際には監視対象ファイルのフィルタリングや、ビルド頻度の調整、ローカル実行時はカレントブランチだけのビルドに限定するなどの工夫を入れたので、300行近いビルドスクリプトになってしまいました

*6:昔はGitHubとかが出てくる前はSubversionを運用していたし、GitHubもしばらくはプライベートリポジトリが3つしか作れないという制限があったので。

*7:執筆はキャディとは関係ない個人活動ですので、できるかぎりコストを抑えたいのです

*8:正確には、GitLabではMergeRequestと呼びます。

*9:PDF化を手作業でやるのは面倒だったので、chromedpを使ってChromeを操作し、PDF出力を自動化する簡単なツールをGoで作成しました。なお、よく利用されているAsciiDocのプロセッサであるAsciidoctorにはPDF出力機能がありますが、Antoraはサイトジェネレーターであるため、HTML出力しかできません。色々試した結果、ChromeのPDF化機能を使うのが一番確実だという結論になりました。

*10:ひょっとしたら、私がやり方を知らないだけかもしれませんが、色々試してもダメでした。