このエントリは、ex-mixi Advent Calendar 2017の16日向けに書いたものです。

ex-mixi Advent Calendar 2017は、ミクシィを卒業したメンバーが次々と近況などを書いているカレンダーです。僕はミクシィに2009年に入社して2013年まで在籍していました。もう少しで4年間ってところの手前で離れた感じです。その後、LINEに移って、mixi Platformの経験を活かして、LINE Platformの構築を3年ほどやってました。未だに「あ、OpenSocialの人だ」って指をさされることも多いですが、そんな感じです。

前の日、つまり15日分の投稿は、エンジニアを辞めて1年が経ちました ~ ex-mixi Advent Calendar 15日目でしたが、僕のIDが3回も登場していて、朝読んだんですけど笑ってしまいました。ソフトウェアエンジニアとしてではないプロダクトの関わり方をしている姿を間近で見ていましたが、素晴らしかったです。やっぱり、ソフトウェアエンジニアリングを経験している別職種の人って、最強だと思うんですよね。1回目の面接をしたことは残念ながら全く覚えていないのですが(本人に謝罪済み)、そのときの自分の判断に間違いはなかった、と過去の自分を褒めたいです。そして、今は別の場所にいますが、これからも応援したいと思っています。早く肉食べに行こうよ。

ところで、今年の10月に技術評論社から「ソーシャルアプリプラットフォーム構築技法」という書籍を発売しました。それからもう2ヶ月になります。この本の内容は、ミクシィにて他の社員の方々と知恵を絞り出しながら作ってきたmixi Platformを世に出し、そして運用していく過程で培ってきたこと、その後LINEに移ってから経験したこと、などなどをベースにして、それらを幅広い方々が読んで活かしていけるように・・・、と思って書きました。っていうのが建前で、本音は「自叙伝的なものを書いてみたかった」という理由です。今までに得た知識を一度アウトプットすることで、この業界で生きてきた証を残したかった、って感じです。まあ、何にせよ無事出版できて良かった思うと同時に、ミクシィで出会った人達には感謝しかありません。

僕と共に作ってきた方々全員に献本差し上げたい気持ちですが、それは難しいことなので、買ってください。

さて、このエントリでは、如何にしてこの書籍を執筆したのか、その舞台裏を紹介してみたいと思います。ポイントは、「40万文字を超える文章量を何を使ってどのように書いていったのか」についてです。原稿用紙にして1000枚以上にもなる量ですが、それをどう書いていったのか、もし「本でも書いてみっかなー」と思っている方がいらっしゃいましたら、参考になるかと思います。

最初の企画段階

2016年の3月、そろそろLINE株式会社を辞めるかなってタイミングで、本書の執筆を思い立ちます。前々から「ミクシィやLINEでやってきたことをまとめて形にしてみたい」と漠然と思っていたのですが、何らか章立てがないと企画を持ち込むにも無理だな、と思って、まずは「こんな内容かなー」というのを書き出しました。以下が、そのときのマインドマップです。

ex_mixi_1.png

小さくてよくわからないですよね?はい、でも、内容はここでは問題ではなく、「このマインドマップを何で書いたか」です。

これは、MindMeisterっていうウェブサービスを使って書きました。当時は画像ファイルへのエクスポート機能があったのですが、ある日突然「エクスポート機能が有料」になってしまいます。無料でエクスポートできるのは、rtf(Rich Text Format)のみ。これだけのためにお金を払うのものなぁ、と思い、他に手軽そうなウェブサービスがなかったので、自分で作りました。

MindMap Tab - Source Code

マインドマップを書いて画像でエクスポートできるChrome拡張機能です。これを使って、MindMeisterから移行しました。

ex_mixi_2.png

というわけで、一刻も早く執筆に取りかからなければならないのに、そのためのツールを自作する、という寄り道をしてしまいました。その代わり、このマインドマップを使って、章立てを組み立て、再考し、そしてどこまで書いたかのTODOリストを管理するのに、自分が最もしっくり来るツールで行うことができました(自作したので当然ですが)。上記の画像で緑色のチェックマークが全部付いていますが、これは書き進めながら一つずつチェックをつけていった結果です。「書いても書いても終わりが見えないよぉ〜」と泣きながら。

ex_mixi_3.png

実は上図のように、赤のバッテンが付いている箇所もあります。そこの部分は、書こうと思ってたけどやっぱりやーめた、っていう章です。サンドボックスについては、確かに書きませんでした。実は読みたい人多かったかもなぁ、と思いながら、まあいっか、と。

絶賛執筆中の段階

章立てがだいたい固まったところで、いよいよ執筆を開始します。が、ここで大問題があります。「何のツールを使って執筆するか」です。

過去に書籍の執筆をしたことがあるのですが、その際には以下のようなツールを使って執筆しました。

  • 2009年 OpenSocial入門 - 本文はテキストエディタで執筆。フォーマットは独自形式。図は、Omni Graffle で作画。
第1章 OpenSocialとは

Googleが発表した「OpenSocial」とは、普段SNSを利用している人々にとって何を提供し、どのような像を
描いているのでしょうか?この章では、OpenSocialが登場した背景や、OpenSocialの目的、そしてSNS用者
にとってのメリットなどを説明します。

1.1 背景

「OpenSocialとは何か」を紹介する前に、何故OpenSocialといったものが登場してきたのか、その背最初に
取り上げてみましょう。そこには、インターネット上でのアプリケーションプラットフォームの進化がしています。

1.1.1 今までのSNS

日本において、最も流行しているSNSと言えば「mixi」であることは、だれも疑わないことでしょう。数やコ
ミュニティの数など、どれを取ってもmixiが日本では最大手のSNSです。しかし、視野を世界に向けてと、・・・

<図1> 世界の大手SNS </図>

<表7> Content要素の属性

属性 | 説明
--------------------------------------------------------------------
type | コンテンツの種別です。"html"もしくは"url"を値として指定します。
view | コンテンツの適用先となるビューの名前です。
href | 外部のコンテンツのURLです。

</表>

<リスト29> 基本項目以外の会員情報項目の取得要求
var params = {};
params[opensocial.DataRequest.PeopleRequestFields.PROFILE_DETAILS] = [
      opensocial.Person.Field.AGE,
      opensocial.Person.Field.GENDER,
      opensocial.Person.Field.CURRENT_LOCATION];
var req = opensocial.newDataRequest();
req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER, params), "viewer")  req.send(function(data) {
   // 取得結果に対する処理
});
</リスト>

ex_mixi_4.png

=== コンテキストメニュー

コンテキストメニューモジュールは、Chromeのコンテキストメニューに項目を追加することを可能にします。

開発者は、コンテキストメニューへの追加を適用するオブジェクトの種別を選択することができます。例えば、
画像、ハイパーリンク、そしてページ全体、といった種別があります。

//table[support][]{
アプリ   拡張機能    必要なパーミッション  名前空間
-------------------------------------------------------------
o   o   contextMenus    chrome.contextMenus
//}

//list[override_pages_manifest][manifest.jsonファイル内でのオーバーライドページ利用の宣言]{
{
  ...,
  "chrome_url_override": {
    "pageToOverride": "myPage.html"
  },
  ...
}
//}

最近では、Re:VIEWで書いてGitHubにPushし、CI使って自動的に組版してPDFやEPUB化をしてデザイン済みのものを見る、っていう繰り返しをやって仕上げていく方法が度々ブログなどで紹介されているのを目にします。確かに、これであれば、PDFになった後のデザインが入ったものを見れば、製本されて売り出されていくイメージが明確にわかりますし、元の原稿はテキストなので、如何様にも自分で解析して整形や一括置換などがやりやすいと思います。

が、しかし、僕は上記のように過去2回テキストエディタで書いた経験に対して、ネガティブな感想を持っていました。特に以下の点です。

  • テキストファイルなので、表やコード、図などは、原稿を書いている際に「出来上がりに近い状態」で見ることができない(当たり前だけど、図なんて一緒に表示すらできない)。
  • 表やコード、図などのために、余計なマークアップが混入する。これによって、執筆した文字数を正確に計算することができない。
  • 原稿と、PDFやHTMLなどの出力結果との間の行ったり来たりが苦痛すぎる。
  • GitHubにpushし忘れてしまうと、外出先で別PCを使って書こうとしても続きを書けなくなる。

大量の文章を書いていく中で、上記は本当に僕にとって致命的でした。最初は「プログラミング時のコンパイルみたいだ」って感じで変換するのを喜んでたんですけど、あっという間に苦痛以外の何者でもなくなりました。結局、僕が求めていたツールは、WYSIWYGだったんです。具体的には、上記を全てひっくり返した、つまり以下の要求を満たすツールでした。

  • 原稿を書いている最中でも、表やコード、図を含めて「出来上がりに近い状態」で見ることができる。
  • 表やコード、図などは「それらだけが純粋に挿入されている」だけであり、文字量を正確に計測することができる。
  • 原稿そのものが「出来上がりに近い状態」であり、別のものと行ったり来たりする必要がない。
  • 特別なことを全くしなくても、複数のPCから続きをすぐに書き始めることができる。

上記を満たしてくれるツール、それは僕の中では一つの選択肢しか思い浮かびませんでした。そう、 Google Docs です。

ex_mixi_5.png

Google Docsでの執筆

Google Docsで執筆をすると決める前に、念のため出版社の編集担当の方に「Google Docsで書いてもいいですか?」と聞き、「何でもいいですよ」という返事をいただいたので、Google Docsで書いていくと決めました。そこからは、もう迷いません。ひたすらGoogle Docsで書き進めるのみです。その際に、面倒なことは嫌なので、以下の点について注意しました。

  • 余計な見た目調整やデザイン変更などは一切しない。Google Docsを新規に作った直後の状態のまま書き切る。
  • 自分で細かな調整も一切しない。その代わり、便利そうなアドオンを使う。

書き始める際に、まず気になるのは「見出し」です。これは、Google Docsだと、確か見出し1から見出し5まであります。予め最初にマインドマップで見出しの構成を作ってあるので、それからブレないようにすることと、深掘りしても1階層のみ、っていう決まりを自分の中で作りました。つまり、今回の執筆で使ったのは、見出し1から見出し4です。そして、見出し1と見出し2のレベルで、Google Docsの文書を分割しました。今回の書籍は、見出し1レベル、つまり章の数は6個、その中に平均4個程度の見出し2レベルが来るので、 6 + 6 * 4 = 30 、つまり30個ちょいの文書に分けて書きました。

ex_mixi_6.png

この文書数、かなり絶妙だったな、と振り返ることができます。というのも、Google Docsは以下の特性があるのを知っていたからです。

  • 1文書あたりだいたい2万文字を超えたあたりから、編集している際の応答速度が落ち始める。

つまり、1文書に4万文字も書いてしまうと、キーボードを打つ度に固まる状態になります。これだと、もう書いてられません。3万台後半までは、ギリギリ耐えられる程度ですが、結構ストレスです。事前にこの制約を知っていたので、見出し2(例: 「4.2 Android向けSDK」というレベル)で分割する = 見出し2レベルは最大1万文字以内にする(全体で30万文字程度)、っていうルールを課したことになります。

結果的には、見出し2レベルでそれぞれ4万文字近くまで書いてしまい、全体的に大きく文字数オーバーとなり、かなり削ることになりました。つい書きすぎてしまう癖は、昔からです。

Google Docsは、事前に適切な章立てがあって、その章立てごとの文章量がある程度最適に見積もられている状態であれば、こんな良い環境はないな、って思ってます。もちろん、現状このような環境が無料で使えるのはGoogle Docsくらいなのでそれを選びましたが、例えば一太郎がGoogle Docs的に使えるのであれば、僕は一太郎を使ったかも知れません。

使ったアドオン

Google Docs向けに使えるアドオンとして、コードの整形をしてくれる Code Pretty を使いました。

ex_mixi_7.png

文字数をカウントするために、Better Word Count を使いました。

ex_mixi_8.png

どちらも無料です。でも、しっかりと期待に応えてくれました。

執筆後の索引作成

OpenSocial入門本の時は、自分で索引を作ることはしませんでした。その代わり、出版社の担当の方が作って、その確認だけをした、って感じでした。そのため、索引を作る(索引に載せる言葉を選ぶ)っていうことをしなければならないってことは、完全に僕の頭の中にはなく、全て書き終わってから「索引お願いします」と依頼され、「何をすれば良いんですか?」と慌てた感じとなりました。

索引って「何か素敵なツールが出版社側にあって、勝手に機械的に作ってくれるもの」だと思っていました。しかし、実際には執筆者が索引に載せたい言葉を選びます。本屋に行って、索引を見て「あー、こんなことが書かれているのね」って把握する人も一定数いるでしょうから、実は索引にどんな言葉を載せるのかは、とっても大事な作業です。

ただし、索引に載っている言葉の数は、結構多いです。平仮名48文字と英数字36文字で84個、それぞれ平均5個の言葉とすると、420個の言葉が必要になります。目の前にある30万文字以上の執筆物から、重要と思われる420個の言葉を抜き出して一覧にしてください、と言われても、必死に考えて出せたとしても100個にも満たない言葉の数だと思いますし、実際チャレンジしてみましたが、僕はそこまでも行きませんでした。どうしよう、焦るばかりです。

そこで、僕は機械的に重要そうと思われる言葉の候補を抽出することを考えます。抽出のルールとしては、以下かな、と考えました。

  • 本文中に出現する名詞について、その出現順の上位は、きっと重要な言葉ばかり。
  • 見出し1から見出し4で使われている言葉は、明らかに重要な言葉ばかり。

まず、前者から取り組みました。これは、原稿の中の全ての文に対して形態素解析を次々とかけていって、名詞を取り出します。そして、その出現数をカウントアップしていって、最後に出現数の多い順でソートし、上位500個を表示するようにしてみます。ただし、単純に名詞を抽出してしまうと、「ソーシャルアプリプラットフォーム」という言葉があった際に、

  • ソーシャル
  • アプリ
  • プラットフォーム

と3つに分解されたまま、それぞれの出現数しか計算できません。そこで、プログラムとして「名詞が続いた場合は、前の名詞と連結させて新しい名詞とする」って感じにしました。これにより、「ソーシャルアプリプラットフォーム」という言葉は、以下のように分解されます。

  • ソーシャル
  • アプリ
  • ソーシャルアプリ
  • プラットフォーム
  • ソーシャルアプリプラットフォーム

Rubyでコードを書き、Google Docsの原稿を全てテキスト形式にエクスポートして、言葉を抽出させました。その結果、以下のような言葉が出てきました。出現数の多い順の上位50個です。実際には、「アプリケーション」という言葉は全て「アプリ」に統一したので、下記の結果はちょっと古い原稿に対して行ったものです。

アプリケーション, 2161
プラットフォーム, 2006
ユーザ, 1657
API, 691
アプリケーション開発, 542
アプリケーション開発者, 533
サービス, 417
BOT, 362
サーバ, 345
プラットフォーム提供, 330
プラットフォーム提供者, 317
アクセストークン, 282
メッセージ, 260
SDK, 255
URL, 204
ユーザ認証, 176
チーム, 163
認証認可, 147
コンテンツ, 147
アクセス, 132
サポート, 117
基本的, 116
具体的, 116
OAuth, 112
可能性, 111
サイト, 107
パラメータ, 106
リスク, 106
*アプリケーション, 102
HTTP, 102
BOTプラットフォーム, 102
インシデント, 102
認可画面, 101
ログイン, 100
アプリ, 100
ポイント, 100
Authorization, 99
メッセージングアプリ, 93
OAuth2, 93
個人情報, 93
Grant, 92
プラットフォーム側, 90
Code, 88
OAuth2.0, 88
OAuth2., 88
スマートフォン, 85
スマート, 79
BOTサーバ, 78
フォン, 78
ドメイン, 78
・・・

ノイズ的なものは無視して、あとは上から順に目視していって「索引に載せるべきものだな」と思った言葉と、その言葉が最初に出てきたページ、あるいはその言葉を定義しているページを検索で見つけていって、索引に載せる言葉の候補としていきました。これに追加で、見出しを頭から見ていって、上記の機械的に抽出した言葉以外で見出しに使っていた言葉を見つけていって、同様に出現ページを探していきました。が、実際にはそのほとんどが上記の結果に含まれていたと記憶しています。

以下、言葉の抽出に使ったRubyコードです。

# coding: utf-8
require 'natto'

#file_name_list = [
#  ['1-0', '1-1', '1-2', '1-3', '1-4'],
#  ['2-0', '2-1', '2-2', '2-3', '2-4', '2-5'],
#  ['3-0', '3-1', '3-2', '3-3', '3-4'],
#  ['4-0', '4-1', '4-2', '4-3', '4-4'],
#  ['5-0', '5-1', '5-2', '5-3', '5-4', '5-5'],
#  ['6-0', '6-1', '6-2', '6-3', '6-4']
#]

file_name_list = [
  ['1-0', '1-1', '1-2', '1-3', '1-4',
  '2-0', '2-1', '2-2', '2-3', '2-4', '2-5',
  '3-0', '3-1', '3-2', '3-3', '3-4',
  '4-0', '4-1', '4-2', '4-3', '4-4',
  '5-0', '5-1', '5-2', '5-3', '5-4', '5-5',
  '6-0', '6-1', '6-2', '6-3', '6-4']
]

def calc(files)
  nm = Natto::MeCab.new
  dict = Hash.new

  files.each do |file_name|
    File.open("#{file_name}.txt") do |file|
      file.read.split('。').each do |sentence|
        n_str = ''
        nm.enum_parse("#{sentence.strip()}。").each do |n|
          current_n = n.feature.start_with?('名詞')
          if current_n
            surface = n.surface
            cnt = dict[surface] || 0
            cnt += 1
            dict[surface] = cnt

            n_str = "#{n_str}#{surface}"
            unless n_str == surface
              cnt = dict[n_str] || 0
              cnt += 1
              dict[n_str] = cnt
            end
          else
            n_str = ''
          end
        end
      end
    end
  end

  array = Array.new
  dict.each do |key, value|
    if key.size > 2 && !key.include?('図') && !key.include?('コード')
      array << {
        name: key,
        value: value
      }
    end
  end

  array.sort! do |a, b|
    b[:value] <=> a[:value]
  end

  array
end

file_name_list.each do |files|
  array = calc(files)[0, 500]
  puts array.map {|n| "#{n[:name]}, #{n[:value]}"}.join("\n")
end

上記の作業で、索引に載せる言葉の抽出を無事行うことができました。ただ、仮に次に執筆するときには、書きながら「これは索引に載せるべきだ」と思った言葉をハイライトしていく、っていうことをした方が、きっと圧倒的に効率的だとは思ってます。

まとめ

ex-mixi Advent Calendarの16日目ということで、書いてみましたが、ほとんどミクシィと関係ない話になってしまいました。が、たぶん、current-mixiおよびex-mixiな方々は、僕の本を立ち読みしていただければ、「あー、わかるわかる」って思っていただけると思います。むしろ書きすぎだと怒られてしまいそうですが、見逃してください(大丈夫だとは思いますが)。このエントリは、ミクシィ時代の思い出を書いた作業自体の思い出を書いてみた(?)、というメタ的なものだと思っていただければ幸いです。

明日は、我らが「はーや先生」です。楽しみですね!

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.