こんにちは。LINE Plus Tech Content StrategyチームのSung Chang Haです。私たちのチームはテクニカルライターで構成されています。LINE Plusで開発したさまざまなプラットフォームの技術ドキュメントを作成する傍ら、社内用語集「Words」のコンテンツ やLINEヤフー Tech Blogの韓国語版コンテンツを担当しています。この記事では、LINE PlanetというVoIPプラットフォームのドキュメントにシングルソーシング(single sourcing)を適用し、ドキュメント管理の効率を改善した事例を紹介します。
シングルソーシングとは?
シングルソーシングとは、単一のソースから複数の成果物を生成することを指します。ドキュメンテーションでは、以下の2つのことを意味します。
- 単一のソースから複数のフォーマット(HTML、PDF、EPUB など)でドキュメントを生成すること。マルチチャンネルパブリッシング(multi-channel publishing)とも呼ばれます。
- 単一のソースからコンテキストに応じて複数のドキュメントを生成すること。ここでのコンテキストは、ドキュメンテーションの要件に応じて定義します(デプロイ環境、動作プラットフォームなど)
以前は主に前者の意味で使われていましたが、最近では後者の意味も含め、より広く使われている用語です。この記事での「シングルソーシング」は後者の意味です。
シングルソーシングの目的
シングルソーシングの主な目的は、ドキュメント管理の効率化とドキュメントの品質を向上させることにあります。
- ドキュメント管理の効率性という点では、「コピーアンドペースト」操作を減らすことで、メンテナンスを簡素化できます。コンテンツを再利用することで、不必要な反復作業を減らし、時間を節約できます。
- ドキュメント品質という点では、単一のソースから情報を取得することで、それぞれのドキュ メントが別々に作成された場合よりも、内容やスタイルの一貫性を高め、エラーを削減できます。
シングルソーシングが必要な場合
以下のような場合にシングルソーシングを適用できます。
- 共通する部分が多いが、多少の違いがある製品群のドキュメントを管理する場合
- 例:high-end_A/B/C、mid-end_A/B/C、low-end_A/B/C製品のドキュメント
- ソフトウェアのデプロイ環境に応じて、適した内容のドキュメントを作成したい場合
- 例:dev/staging/prod環境別ドキュメント
シングルソーシングの主な手法
シングルソーシングの主な手法には、以下のようなものがあります。
- 条件付きコンテンツ(conditional content)
- 変数処理(variables)
- コンテンツの再利用(content reuse)
それぞれについてさらに詳しく説明します。
補足:
- 商用・独自(proprietary)のドキュメンテーションツールまたは静的サイトジェネレーター(static site generator、SSG)において、これらの手法を提供する方法やドキュメント内で使用するための文法はさまざまです。そのため、この記事では特定のツールの文法を例に挙げるより、理解を助けるための擬似コード(pseudocode)を使いました。
- 商用・独自のドキュメンテーションツールが提供するシングルソーシング機能について興味を持たれている場合は、以下のリンクをご参照ください。
- Adobe FrameMakerのSingle-sourcing content
- WritersideのSingle sourcing and content reuse
条件付きコンテンツ
一定の条件によってコンテンツを含めるかどうかを決定するフィルタリング処理を意味します。以下のような場合に活用できます。
- 特定の開発環境にのみ該当する内容がある場合
- マルチプラットフォームのドキュメントで、プラットフォームごとに少しずつ違いがある場合
例えば、「staging」環境のドキュメントにのみ含める内容を、条件付きコンテンツとして管理することを疑似コードで表現すると、以下のようになります。
// メタデータや設定ファイルでコンテキストを定義
env: ['dev', 'staging', 'prod']
// ドキュメントで条件付き構文を使用
<if env="staging">
{content}
</if>
変数処理
値や用語を変数化することを意味します。以下のような場合に活用できます。
- SDKごとに最新バージョンを変数で管理する
- 特定の機能を使用するために必要な最小SDKバージョンを変数で管理する
- 連絡先、カスタマーサポートチャネルなどのURLを変数で管理する
例えば、SDKの最小バージョンを変数で処理することを疑似コードで表すと、以下のとおりです。
// メタデータや設定ファイルで変数を宣言
minimum_version: 2.0
// ドキュメントで変数を使用
Use the SDK <var name="minimum_version" /> or higher.
コンテンツの再利用
よく使うコンテンツを再利用することを意味します。以下のような場合に活用できます。
- 複数のページに同じく表示される案内を再利用する
- ガイドやチュートリアルに繰り返される事前準備(prequisites)を再利用する
例えば、古いドキュメントについての案内を再利用することを疑似コードで表すと、以下のとおりです。
// 再使用するコンテンツを定義
<note id="deprecation-note">
This page has been deprecated.
</note>
// ドキュメントでコンテンツを再利用
<include element-id="deprecation-note" />
再利用するコンテンツは、上記の例のように案内文一つになることもありますが、複数の段落またはページ単位になることもあります。
LINE Planetとドキュメントサイトの紹介
LINE Planetは、音声通話およびビデオ通話機能を提供するVoIPプラットフォームで、サービス型コミュニケーションプラットフォーム(CPaaS)です。クラウドベースで1:1通話、グループ通話、画面共有などさまざまな機能を提供しています。また、クライアントアプリ開発のために、以下のようにさまざまなプラットフォームをサポートするSDKを提供しています。
- ネイティブプラットフォーム
- モバイルOS:iOS、Android
- デスクトップOS:macOS、Windows
- ウェブ
- クロスプラットフォームフレームワーク
- Flutter
LINE Planetプロダクトの特性のうち 、ドキュメントサイトの構築や運用に影響するものは、以下のとおりです。
- ネイティブ、ウェブ、Flutterの間で提供する機能や詳細な実装方法に違いがある
- ネイティブプラットフォームから一番多くの機能を提供し、ウェブやFlutterからは主に部分集合に該当する機能を提供します。
- 一つの機能内でも、プラットフォーム間で細かい実装方法に少し違いがあります。
- プラットフォームによってAPIのネーミングに少しずつ違いはあるが、機能ごとに比較的均一に設計されている
- 例えば、1:1通話で電話をかけるためのメソッド名は
makeCall()
またはMakeCall()
でほぼ同じです。また、通話が接続されたときに発生するイベント(コールバック)の名前はonConnected
、didConnect
、OnConnected
、evtConnected
など、特定の表現を含み、プラットフォームごとに使用されるプログラミング言語のコーディング規約に従っています。 - まったく異なる名前を使う場合や、他のプラットフォームでは単一のメソッドで実現されている機能を特定のプラットフォームでは複数のメソッドに分割する場合など、例外的なケースは稀です。
- 例えば、1:1通話で電話をかけるためのメソッド名は
LINE Planetドキュメントサイト(以下、Planet Docs)は、LINE Planetの技術ドキュメントを提供するウェブサイトで、プロダクトの概要、はじめる、SDK、サーバーAPI、ヘルプなどの領域で構成されています。
ドキュメントサイトの要件
ドキュメントサイトは、以下のようにバージョン管理が必要な領域(SDK)と、そうでない領域に分かれています。SDKのドキュメントの場合、以前のバージョンを使用する顧客の参考になるよう、ドキュメントをバージョンごとに提供しています。
領域 | 説明 | ドキュメントバージョンの区分 |
---|---|---|
概要 | LINE Planetの主な特徴、機能一覧、主な用語の定義などを含む概要を提供します。 | バージョン管理なし* |
はじめる | LINE Planet連携を開始するのに役立つ領域で、開発環境や認証情報を提供し、デモアプリを紹介します。 | バージョン管理なし |
SDK | アプリケーションクライアントを実装するためのガイドと参照ドキュメントをプラットフォームごとに提供します。 | 各プラットフォームに対応するSDKバージョンごとのドキュメントを提供** |
サーバ ーAPI | サーバー側との連携のためにガイドと参照ドキュメントを提供します。 | バージョン管理なし |
ヘルプ | よくある質問と回答、トラブルシューティング資料、連絡先の情報を提供します。 | バージョン管理なし |
* バージョン管理なしの領域では、最新の情報のみを提供します。
** プロダクトの特性上、プラットフォームごとにバージョン管理が異なります。例えば、ネイティブプラットフォーム向けのSDKは最新バージョンが5.5ですが、ウェブ向けのSDKは5.3、Flutter向けのSDKは0.9です。
さらに、ドキュメントサイトを日本語、英語、韓国語の3つの言語でサポートするために多言語化も必要です。
Docusaurusを使用する理由
Planet DocsのドキュメンテーションツールとしてDocusaurusを使用しています。Docusaurusを使用する最大の理由は、 LINE Plusのテクニカルライティング組織でDocusaurusを共通のSSGとして選定し、使用しているためです(参考:技術ドキュメントサイトの構築にDocusaurusを活用する(韓国語))。
上記の記事で紹介したように、DocusaurusはReactコンポーネントを開発して機能を拡張できるというメリットがあります。また、Planet Docsの要件を満たす、以下のような機能をデフォルトで 提供する点もDocusaurusを選択する上で重要なポイントでした。
- マルチインスタンスとバージョン管理機能により、プラットフォームごとにインスタンスを定義してSDK領域からプラットフォームごとにドキュメントを提供できます。また、プラットフォームごとに異なるバージョンセットで構成されたドキュメントを提供できます(デフォルトインスタンスではバージョン管理なしのドキュメントを管理します)。
- マークダウンインポート機能でコンテンツを再利用できます。Docusaurusでは、アンダーバーで始まる名前のマークダウンファイルに再利用するコンテンツを作成し(「partial」と呼ばれるファイル)、他のマークダウンファイルからそのファイルをインポートして再利用できます。
Planet Docsの場合、各プラットフォームに対応するインスタンスを定義していますが、iOSとmacOSはドキュメントの内容がほとんど似ているため、1つのインスタンスとして管理しています。Planet DocsでサポートするSDKプラットフォームとそれぞれのインスタンスおよび提供バージョンは、以下のとおりです。
SDKプラットフォーム | インスタンス名 | 提供バージョン(2025年3月現在) |
---|---|---|
Android | android | 4.4, 5.0, 5.1, 5.2, 5.3, 5.4, 5.5 |
iOS, macOS | iosmacos | 4.4, 5.0, 5.1, 5.2, 5.3, 5.4, 5.5 |
Windows | windows | 4.4, 5.0, 5.1, 5.2, 5.3, 5.4, 5.5 |
ウェブ | web | 4.2, 5.0, 5.1, 5.2, 5.3 |
Flutter | flutter | 0.6, 0.7, 0.8, 0.9, 1.0 |
Planet Docsに適用したシングルソーシングの事例紹介
シングルソーシング適用の必要性
前述のように、SDKのドキュメントの場合、バージョン管理と3つの言語に対応する多言語化が必要です。SDK全体のプラットフォーム数は6つ(iOS、Android、macOS、Windows、ウェブ、Flutter)ですが、上の表のようにiOSとmacOSは1つのインスタンスとして管理し、バージョンの差が大きいFlutterは別途管理しています。その結果、シングルソーシングが適用されているドキュメントインスタンスは、現在4つ(android、iosmacos、windows、web)です。
このような状況でシングルソーシングの未適用・適用の状態それぞれについて、単一のバージョンで1つの変更を適用する際に修正が必要なドキュメントファイルの数を計算してみると、以下のとおりです。
- シングルソーシング未適用:インスタンス数(4個) * 言語数(3言語) = 12個
- シングルソーシングの適用:単一共有ドキュメント(1個) * 言語数(3言語) = 3個
実際には、古いバージョンのドキュメントを一緒に修正する必要がある場合や、今後サポートするプラットフォームや言語が増える可能性もあります。そのため、シングルソーシングを適用しない場合は、シングルソーシングを適用した場合よりも、修正が必要なドキュメントの数がはるかに多くなります。複数のファイルにそれぞれコピーアンドペーストして、違いを反映するような編集作業をしていると、エラーや一貫性に矛盾する問題が発生することもあります。そのため、シングルソーシングを適用する方がさまざまな面で有利だと判断しました。
シングルソーシングの設計
前述のように、LINE Planetプロダクトには以下のような特性があります。
- ネイティブ、ウェブ、Flutterの間で提供する機能や詳細な実装方法に違いがある
- プラットフォームによってAPIのネーミングに少しずつ違いはあるが、機能ごとに比較的均一に設計されている
各特性に応じて、以下のようにシングルソーシング手法を適用することを考えられます。
- ネイティブ、ウェブ、Flutterの間で提供する機能や詳細な実装方法に違いがある → 条件付きコンテンツ
- 共有ドキュメントで条件付き構文を利用して、特定のインスタンスにのみ含まれる内容や、特定のインスタンスでない場合にのみ含まれる内容を記述します 。
- プラットフォームごとに生成されたドキュメントには、そのプラットフォームに該当する内容のみ含まれます。
- プラットフォームによってAPIのネーミングに少しずつ違いはあるが、機能ごとに比較的均一に設計されている → 変数処理
- API項目ごとにIDを付けて、IDと実際の名前をマッピングするメタデータをプラットフォームごとに用意し、共有ドキュメントではAPIを変数化して作成します。
- プラットフォームごとに生成されたドキュメントでは、変数が各プラットフォームの実際のAPI名に置き換えられます。
これをベースにしたPlanet Docsのシングルソーシングの仕組みは、以下のとおりです。
- 条件付きコンテンツと変数処理のためのReactコンポーネントを開発し、API情報を含むメタデータを用意します。
- 条件付きコンテンツと変数処理コンポーネントを使って、共有ドキュメントを作成します。
- プラットフォームごとに、共有ドキュメントをインポートするプレースホルダードキュメントを作成します。
- ビルドを実行すると、条件付きコンテンツと変数を処理してプラットフォームごとに実際のドキュメントが生成されます。
Planet Docsでは、このようなプラットフォーム情報に加え、バージョンもコンテキストの一部です。したがって、現在のプラットフォームは何か、そのプラットフォーム内の現在のバージョンは何か、この2つによって全体のコンテキストが決まります。
シングルソーシングの実装と適用
Planet Docsでは、以下のようにシングルソーシングの実装・適用しました。
条件付きコンテンツ
条件付き処理では、インスタンスをベースにフィルタリングするかどうかを決めます。前述のように、Planet Docsでシングルソーシングを適用するドキュメントインスタンスは、以下の4つです。
補足:Docusaurusのマルチインスタンス機能で定義したインスタンス情報とインスタンスごとに作成されたバージョン情報は、Docusaurus内部で管理されます。そのため、実際は以下のように別途設定するのではなく、Docusaurusの内部APIを使って取得します。
// コンテキストを定義(インスタンス)
platform: ['android', 'iosmacos', 'windows', 'web']
上記のインスタンスをベースにフィルタリングを行う<Conditional>
コンポーネントを実装し、そのコンポーネントは共有ドキュメントで以下のように使用できます。
// ドキュメントで条件付き構文を使用
<Conditional ifNotAppliesTo="web">
{content for non-web platforms}
</Conditional>
<Conditional ifAppliesTo="web">
{content for web}
</Conditional>
変数処理
変数処理では、インスタンスとバージョンの両方がコンテキストを決定するために必要です。以下のように、インスタンスおよびバージョンごとにAPI情報を含むYAML形式のメタデータを作成します。
// メタデータに変数を定義
# android, 5.5
makeCall:
name: makeCall()
evtConnected:
name: onConnected
# iosmacos, 5.5
makeCall:
name: makeCall()
evtConnected:
name: didConnect
# windows, 5.5
makeCall:
name: MakeCall()
evtConnected:
name: OnConnected
# web, 5.3
makeCall:
name: makeCall()
evtConnected:
name: evtConnected
上記のようなメタデータを読み込んで、IDに対応する実際のAPI情報に置き換える <Api>
コンポーネントを実装しました。そのコンポーネントは、共有ドキュメントで以下のように使用できます。
// ドキュメントで変数を使用
After calling <Api id="makeCall" />, the <Api id="evtConnected" /> event occurs.
コンテンツの再利用
Planet Docsでは、シングルソーシング手法の紹介で取り上げた例のように、小さな単位のコンテンツを再利用するのではなく、ページ単位で再利用する方法を使います。以下のように、条件付きコンテンツや変数処理コンポーネントを使って共有ドキュメントを作成します。
// 再利用するコンテンツを作成
// _shared/en/{ver}/call-flow/_flow-1-to-1-call.mdx
<Conditional ifNotAppliesTo="web">
After calling <Api id="makeCall" />, the <Api id="evtConnected" /> event occurs.
</Conditional>
<Conditional ifAppliesTo="web">
After calling <Api id="makeCall" />, the <Api id="evtConnected" /> event occurs. (Some other description for web)
</Conditional>
このような共有ドキュメントをDocusaurusのマークダウンインポート機能を利用して、各プラットフォームのドキュメントでインポートし、再 利用します。
// 各プラットフォームのドキュメントでコンテンツを再利用
// {platform}/call-flow/flow-1-to-1-call.mdx
---
title: "1-to-1 call flow"
---
import Content from '@site/_shared/{lang}/{ver}/call-flow/_flow-1-to-1-call.mdx';
<Content />
上記の共有ドキュメントをビルドするとプラットフォームごとに生成されるドキュメントの内容は、以下のとおりです。
Android
iOS/macOS
Windows
ウェブ
シングルソーシングの機能拡張
実際のドキュメンテーションプロセスでは、さらに多様な要件が出てくるものです。ここでは、変数処理に関して機能を拡張した事例を紹介します。
これらの事例を見る前に、APIについてより詳しく説明する必要があります。それに関連して、シングルソーシングが適用されるプラットフォームごとにSDKのAPIがどのようなプログラミング言語で提供され、APIリファレンスがどのようなツールで生成されるかを、以下の表にまとめましたのでご参照ください。
SDKプラットフォーム | APIのプログラミング言語 | APIリファレンス生成ツール |
---|---|---|
Android | Kotlin | Dokka |
iOS/macOS | Swift | Jazzy |
Windows | C++ | Doxygen |
ウェブ | JavaScript | JSDoc |
APIの包含関係の表現
LINE Planet SDKには、1:1通話やグループ通話を処理するメインクラス(それぞれPlanetKitCall
, PlanetKitConference
)と、通話タイプ別のイベント処理APIがあります。しかし、通話接続と接続終了時に発生するonConnected
やonDisconnected
のようなイベントは、両方の通話タイプのイベント処理APIに共通して定義されています。他にも、機能によって、通話タイプごとのメインクラスに同じ名前のメソッドを定義している場合もあります。ドキュメントでは、このようなAPIをどのように区別すれば良いのでしょうか?
オブジェクト指向プログラミング言語において、APIは大きく分けて上位要素(クラス、インターフェース、構造体、列挙型など)と、その下に定義される下位 要素(プロパティ、メソッド、構造体メンバー、列挙型ケースなど)に分けることができます。このように2つのレベルに分けてLINE Planet SDKのAPIメタデータを作成すると、以下のようになります(長くなりすぎないように、Android用のメタデータのみを例に挙げます)。
// 上位要素と下位要素の2レベルで作成したメタデータ
# 通話タイプごとにクラスが区別されないAPI
PlanetKit:
name: PlanetKit
makeCall:
name: makeCall()
verifyCall:
name: verifyCall()
joinConference:
name: joinConference()
# 1:1通話API
PlanetKitCall:
name: PlanetKitCall
startMyScreenShare:
name: startMyScreenShare()
stopMyScreenShare:
name: stopMyScreenShare()
...
CallListener:
name: CallListener
evtConnected:
name: onConnected
evtDisconnected:
name: onDisconnected
...
# グループ通話API
PlanetKitConference:
name: PlanetKitConference
startMyScreenShare:
name: startMyScreenShare()
stopMyScreenShare:
name: stopMyScreenShare()
...
ConferenceListener:
name: ConferenceListener
evtConnected:
name: onConnected
evtDisconnected:
name: onDisconnected
このような2レベル構造のメタデータを処理できるように <Api>
コンポーネントを修正し、共有ドキュメントで以下のように使います。
// _shared/en/{ver}/_some-document.mdx
- In 1-to-1 calls, after calling <Api id="makeCall" />, <Api id="CallListener.evtConnected" /> occurs.
- In group calls, after calling <Api id="joinConference" />, <Api id="ConferenceListener.evtConnected" /> occurs.
2つの要素がより明確に区別できるように、上位要素を一緒に表示したい場合もあります。それに対応するためにshowParent
を追加し、以下のように使います。
// _shared/en/{ver}/_some-document.mdx
- In 1-to-1 calls, after calling <Api id="makeCall" />, <Api id="CallListener.evtConnected" showParent /> occurs.
- In group calls, after calling <Api id="joinConference" />, <Api id="ConferenceListener.evtConnected" showParent /> occurs.
上記のような共有ドキュメントをビルドすると生成されるAndroidドキュメントの内容は、以下のとおりです。
補足:ドキュメントでAPIの上位要素と下位要素の関係を示す表記は標準化されていません。会社やプロジェクトのドキュメントスタイルによって異なる場合があります。プログラミング言語によって点(.)やダブルコロン(::)で区切るなどの表記を使う場合もありますが、Planet Docsでは、LINE Planet開発チームと協 議を経て、上記のように表記しています(実際には、プラットフォームによってstaticメソッドは点だけで区切るなどの細かいルールがあり、 <Api>
コンポーネントにこれらのルールを処理する機能も含まれています)。
これにより、ドキュメントでAPIの包含関係を決まったスタイルに従って一貫性のある表現にすることができます。また、上位要素を表示するかどうかをコンポーネントのプロパティで制御でき、効率的です。
APIリファレンスリンクの提供
次に、ドキュメントに含まれるAPIから、対応するAPIリファレンスの項目へのリンクを設定することを考えられます。LINE Planetでは、各プラットフォームのプログラミング言語に応じて、広く使われているツールを利用してAPIリファレンスを生成しています。マルチプラットフォームプロダクトのドキュメントではこれが一般的です。
前述でメタデータを上位要素と下位要素の2レベルに拡張したため、APIリファレンスの内部構造を把握することで、各API要素のリンクを適切に読み込むスクリプトを作成できます。このようなスクリプトを使ってメタデータにリンクフィールド(path
)を追加した結果は、以下のようになります。
// APIリファレンスリンクが追加されたメタデータ
# 通話タイプごとにクラスが区別されないAPI
PlanetKit:
name: PlanetKit
path: /planet/com.linecorp.planetkit/-planet-kit/index.html
makeCall:
name: makeCall()
path: /planet/com.linecorp.planetkit/-planet-kit/make-call.html
verifyCall:
name: verifyCall()
path: /planet/com.linecorp.planetkit/-planet-kit/verify-call.html
joinConference:
name: joinConference()
path: /planet/com.linecorp.planetkit/-planet-kit/join-conference.html
# 1:1通話API
PlanetKitCall:
name: PlanetKitCall
path: /planet/com.linecorp.planetkit.session.call/-planet-kit-call/index.html
startMyScreenShare:
name: startMyScreenShare()
path: /planet/com.linecorp.planetkit.session.call/-planet-kit-call/start-my-screen-share.html
stopMyScreenShare:
name: stopMyScreenShare()
path: /planet/com.linecorp.planetkit.session.call/-planet-kit-call/stop-my-screen-share.html
...
CallListener:
name: CallListener
path: /planet/com.linecorp.planetkit.session.call/-call-listener/index.html
evtConnected:
name: onConnected
path: /planet/com.linecorp.planetkit.session.call/-call-listener/on-connected.html
evtDisconnected:
name: onDisconnected
path: /planet/com.linecorp.planetkit.session.call/-call-listener/on-disconnected.html
...
# グループ通話API
PlanetKitConference:
name: PlanetKitConference
path: /planet/com.linecorp.planetkit.session.conference/-planet-kit-conference/index.html
leaveConference:
name: leaveConference()
path: /planet/com.linecorp.planetkit.session.conference/-planet-kit-conference/leave-conference.html
startMyScreenShare:
name: startMyScreenShare()
path: /planet/com.linecorp.planetkit.session.conference/-planet-kit-conference/start-my-screen-share.html
stopMyScreenShare:
name: stopMyScreenShare()
path: /planet/com.linecorp.planetkit.session.conference/-planet-kit-conference/stop-my-screen-share.html
...
ConferenceListener:
name: ConferenceListener
path: /planet/com.linecorp.planetkit.session.conference/-conference-listener/index.html
evtConnected:
name: onConnected
path: /planet/com.linecorp.planetkit.session.conference/-conference-listener/on-connected.html
evtDisconnected:
name: onDisconnected
path: /planet/com.linecorp.planetkit.session.conference/-conference-listener/on-disconnected.html
...
<Api>
コンポーネントに、必要に応じてリンクを適用できるようにする withLink
というプロパティを追加し、共有ドキュメントで以下のように使います。
// _shared/en/{ver}/_some-document.mdx
- In 1-to-1 calls, after calling <Api id="makeCall" withLink />, <Api id="CallListener.evtConnected" showParent withLink /> occurs.
- In group calls, after calling <Api id="joinConference" withLink />, <Api id="ConferenceListener.evtConnected" showParent withLink /> occurs.
上記のような共有ドキュメントをビルドすると生成されるAndroidドキュメントの内容は、以下のとおりです。
このような処理がなければ、プラットフォームごとにAPIリファレンスリンクを一つ一つコピーアンドペーストする手間がかかり、ドキュメントで扱うAPIが多ければ多いほど作業が増えるでしょう。メタデータから情報を取得することで、管理効率を高め、エラーが発生する可能性を低減できます。
適用にあたっての前提条件と制約および効果
まず、マルチプラットフォームドキュメントにシングルソーシングを適用するための前提条件から説明します。LINE Planetプロダクトの特性として、「プラットフォームによってAPIのネーミングに少しずつ違いはあるが、機能ごとに比較的均一に設計されている」というものがありました。そのため、APIメタ データを通じた変数処理が可能になるには、API設計とネーミングが均一である必要があります(この場を借りて、より良いAPI設計とネーミングのためにご尽力くださったLINE Planet SDKの開発者の方々に感謝申し上げます)。
次に、制約についても検討が必要です。すべてのタイプのドキュメントにシングルソーシングを適用することは困難です。実装段階に近づくほどプラットフォームごとに異なる部分が多くなり、チュートリアルのようなドキュメントを共有ドキュメントとして作成しようとすると、条件付きコンテンツ構文が乱立して管理が難しくなります。そのため、Planet Docsではコンセプトガイド(コールフロー、サブグループ)と拡張機能ガイドにのみシングルソーシングを適用しています。チュートリアルのようなサンプルコードドキュメントにはシングルソーシングを適用していません(もちろん、それでもシングルソーシングが適用された領域がかなりの分量を占めているので、その効果は大きいと言えます)。
適用効果としては、冒頭で言及したドキュメント管理効率の改善やドキュメント品質の向上に加え、プラットフォーム間の違いをドキュメント内に条件付きコンテンツ構文で指定し、追跡できるという点がメリットです。また、多言語化する際、プラットフォームの数だけのドキュメントではなく、単一の共有ドキュメントを翻訳すればいいので、コストと工数の削減という付加的なメリットもあります。
一方、シングルソーシングが適用されたドキュメントを作成してメンテナンスを行うには、それだけプロダクトのAPI構造をよく把握する必要があります。その分、テクニカルライターの労力が必要になります。同時に、ドキュメントをレビューする開発チームでも、このようなドキュメント構造を認識している必要があります。
おわりに
ここまで、LINE Planetのドキュメントサイトに適用したシングルソーシングについて説明しました。現時点では、APIメタデータのIDと実際のAPI名を入力する段階を手動で処理していますが、AIを活用してAPIリファレンスベースで自動的にメタデータを生成するなど、改善できることがあると思います。その他にも、追加的な要件を反映して、シングルソーシングのためのコンポーネントとドキュメントサイトを継続的に改善していく予定です。
長文でしたが、お読みいただきありがとうございました。この記事が、マルチプラットフォームプロダクトのドキュメントサイトを管理するテクニカルライターや開発者の方、そしてドキュメンテーションとシングルソーシングに興味を持たれている方に参考になれば幸いです。