日々変化するゆるふわフォーマットをBigQueryでおいしく料理する方法。Athenaユーザも必見だよ!

3行まとめ

  • BigQueryはいいぞ
  • 外部テーブルはすごいぞ
  • Scheduled Queryも便利だぞ

こんにちは。ひむ(@himu)です。
株式会社fluctでエンジニアとして働いていたり、ボルダリングしたりガチャを回したり健康で文化的な生活をしています。

fluctはインターネット広告プラットフォームのサービスなどを提供しており、毎日億単位の大量のイベントログが発生しています。
イベントログには、売上の計算に必要なデータから、アプリケーションを改善する上で必要なデータなど、様々なデータが入り混じっており、情報が追加されることも度々あります。
今回は、そんな日々変化するログを分析基盤に取り込み、活用しやすくするためにやってきたことを紹介します。

背景

今回扱うサービスは社内では比較的新しいもので、Athenaでログをクエリするしくみが早い段階から作られていました。

アプリケーションはAWSのEC2インスタンス上で動いており、ログファイルに吐き出したデータをkinesis agentがストリームに取り込み、Kinesis Firehoseを使ってS3に保管するという流れです。
当初は取引量も少なく、S3に置いたログをAthenaでクエリするので十分データを活用できていました。

しかし取引量が増えるにつれてログの量やフィールドが増えたことで、1日あたり10億レコード、1TB近くと、単純にデータサイズが大きくなってきました。
Athenaで分析しようとしても、クエリごとに数分、あるいはそれ以上かかるようになり、データ活用の大きな障害になってしまいました。Athenaのパーティション自動生成のしくみがわかりづらかったことも頭を悩ませる原因になっていました。

そこで、もともとチームの分析基盤としてBigQueryを使っていたこともあり、そちらに取り込もうと考えました。

データの流れ

f:id:himuk:20201002185620j:plain

そのままコピーするだけのLambda

S3からGCSへのコピーでは、今回パスも含めてそのままコピーするLambdaを用意しました。

import io
import json
from urllib.parse import unquote_plus

import boto3
from google.cloud import storage
from google.oauth2 import service_account

s3_client = boto3.client('s3')
sm_client = boto3.client('secretsmanager')


def init_gcs():
    cred_json = sm_client.get_secret_value(SecretId='gcp_serviceaccount_json')['SecretString']
    cred = json.loads(cred_json)
    return storage.Client(
        credentials=service_account.Credentials.from_service_account_info(cred),
        project='project_name',
    )

def handler(event, context):
    gcs_client = init_gcs()

    for record in event['Records']:
        stream = io.BytesIO()

        s3_bucket = record['s3']['bucket']['name']
        key = unquote_plus(record['s3']['object']['key'])
        s3_client.download_fileobj(s3_bucket, key, stream)

        gcs_bucket = gcs_client.bucket('bucket_name')
        blob = gcs_bucket.blob(key)
        blob.upload_from_string(stream.getvalue())

外部テーブルを使おう

ログはJSON Lines(BigQueryでいうところのNEWLINE_DELIMITED_JSON)で、配列データがあったり、デバッグ用のフィールドがomitemptyされたりしています。
また、スキーマの変更もあり得るため、そのたびにテーブルスキーマ定義も変更しないと取り込みエラーになる可能性があります。
いわゆる「ゆるふわフォーマット」というやつですね(たぶん)。

Athenaはそういったデータをクエリするのに便利なツールで、データの実体はクラウドストレージにあり、テーブルはビューのような感覚で使えるようになっています。

GCPにも似た機能があり、それがBigQueryの「外部テーブル(External Table)」です。

まさにGCP版Athenaとも言える機能で、GCSやGoogle Driveに置いたファイルをBigQueryから直接クエリすることができます。
Athenaと同様にhive partitionを扱えて、BigQueryのパーサをそのまま使えるので対応フォーマットも多いです。 少し前にGoogle SheetでBigQueryが使えるようになったりしていて、BigQueryの多機能ぶりには頭が上がらないですね。

クエリ速度ですが、Athenaと比較するのが馬鹿らしくなるくらい速いです。
Athenaで10分以上かかっていたクエリが数秒で返ってきたときは感動しました。
今回はGCSのNearline Storageに置いたので、ストレージクラスが違ったりGoogle Driveなどにクエリすると遅くなるのかもしれません。

ゆるふわをゆるふわのまま扱う

外部テーブルを使えばBigQueryで扱うことができるようになりますが、ゆるふわフォーマットをゆるふわたらしめているのはスキーマで、外部テーブルも例外なくスキーマ定義をしなければなりません(auto detectは使えます)。

正直これが一番面倒な作業です。ログに出しているデータすべてを定義しなくてはならないし、変更にも都度対応しなければなりません。
日々活用しているのは一部のフィールドだけだったりしていて、深くネストされたフィールドなどはデバッグでたまに見るくらいだったりします。

であれば、一部だけクエリできるようにして、あとはJSON文字列のままのほうがクエリしやすくもなるのでは?
必要なフィールドだけパースするようにすれば、エラーの可能性も低くできるのでは?

そこで、「JSONはJSON文字列のまま取り込んで、そのあと必要なフィールドだけ抽出する」ようにしました。
外部テーブルではJSON Linesをそのまま1カラムのレコードとして取り込み、その後BigQueryの関数を使ってパースしたテーブルを定期的に作ります。

こうすれば、外部テーブルは一度作ってしまえばずっと使えるし、パースするクエリを変更したタイミングから新しいスキーマでテーブルが作られるようにできます。

これを実現するために、

  • JSON Linesを1カラムのレコードとして取り込む
  • 定期的に外部テーブルにクエリして結果を保存する

といったことをやっていきます。

JSON Linesを1カラムのレコードとして取り込む

BigQueryでJSONフォーマットを指定すると、すべてのフィールドをパースしようとします。「ここまでパースして!これより深いネストはパースしないで!」というワガママは聞いてくれないのです。

というわけで、「これはJSONフォーマットではありません」という風に教えてあげます。

具体的にどうするかというと、同じ1行1レコードであるCSVを指定します。
BigQueryのCSVフォーマットにはデリミタを指定することができるため、データに入り得ない文字を指定すれば1行1カラムとしてパースされます。

外部テーブルは、通常のテーブルと同様にGCPコンソールやコマンドラインツールから作成することができます。

bq mk --external_table_definition=tabledef.json dataset.table
{
    "csvOptions": {
        "allowJaggedRows": false,
        "allowQuotedNewlines": false,
        "encoding": "UTF-8",
        "fieldDelimiter": "",
        "quote": "",
        "skipLeadingRows": 0
    },
    "schema": {
        "fields": [
            {
                "name": "fields",
                "type": "STRING"
            }
        ]
    },
    "sourceFormat": "CSV",
    "sourceUris": [
        "gs://bucket/*.gz"
    ],
    "hivePartitioningOptions": {
        "mode": "STRINGS",
        "sourceUriPrefix": "gs://bucket/",
        "requirePartitionFilter": true
    }
}

デリミタには制御文字を指定しています。
JSONは制御文字をエスケープしなければならないため、エスケープされていない制御文字をデリミタに指定すれば1カラムのテーブルができます。

……そう、JSONは制御文字をエスケープしなければなりません。
しかし、外部テーブル定義ファイルはJSONです。つまり上の外部テーブル定義はJSONとして読み込めないのです。

File ".../bq/third_party/yaml/lib2/reader.py", line 144, in check_printable
    'unicode', "special characters are not allowed")
ReaderError: unacceptable character #x0001: special characters are not allowed
  in "tabledef.json", position 174

当時のわたしは「一度きりだし、今だけ動けばいいや」とエラーを出しているライブラリを書き換えてコマンドを実行してテーブルを作成しました。
今考えると、BigQueryクライアントライブラリを使えば問題なく作れますね(一度使うためだけに書くのが面倒ではありますが)。試してませんが、ブラウザで制御文字を入力してもできるかもしれません。

定期的に外部テーブルにクエリして結果を保存する

ここまでで、JSONログをBigQueryでクエリすることができるようになりました。
あとは、JSONをパースして必要なフィールドを抽出したテーブルをつくるだけです。

今回は1時間毎にhive partitionがつくられるようになっているため、1時間毎に外部テーブルをクエリして、結果をテーブルに出力するようにしました。

この定期的にクエリを実行するのにはBigQueryの機能である「スケジュールされたクエリ(Scheduled queries)」を使います。

文字通り定期的に指定したクエリを実行してくれる機能で、1日ごと1時間ごとなどの指定はもちろん、特殊な文法ではあるものの任意の間隔でクエリを実行することができます。
これにBigQueryの機能である「Destination Table」を合わせて使うことで、定期的にクエリを実行し、結果をテーブルに書き込むことができます。

クエリには、実行時間をTIMESTAMP型の変数 @run_time として使うことができます。

SELECT
  PARSE_TIMESTAMP('%Y-%m-%dT%H:%M:%E3S%z', JSON_EXTRACT_SCALAR(fields, '$.timestamp')) AS `timestamp`,
  JSON_EXTRACT_SCALAR(fields, '$.message') AS `message`,
  STRUCT<
    `id` STRING,
    `type` STRING
  >(
    JSON_EXTRACT_SCALAR(fields, '$.req.id'),
    JSON_EXTRACT_SCALAR(fields, '$.req.type')
  ) AS `req`,
  JSON_EXTRACT(fields, '$.buyers') AS `buyers`,
  STRUCT<
    `creative_id` STRING,
    `price` FLOAT64
  >(
    JSON_EXTRACT_SCALAR(fields, '$.res.creative_id'),
    CAST(JSON_EXTRACT_SCALAR(fields, '$.res.price') AS FLOAT64)
  ) AS `res`,
  STRUCT<
    `req` STRING,
    `res` STRING
  >(
    JSON_EXTRACT(fields, '$.debug.req'),
    JSON_EXTRACT(fields, '$.debug.res')
  ) AS `debug`
FROM `project.dataset.external_table`
WHERE ts = FORMAT_TIMESTAMP('%Y%m%d%H', TIMESTAMP_ADD(@run_time, INTERVAL -1 HOUR))

Destination Tableのテーブル名にも変数を使うことができます。
UTCからJSTは+9時間ですが、1時間前のデータなので+8hとしています(今回は日別にテーブルを作って append するようにしています)。

table_{run_time+8h|"%Y%m%d"}

まとめ

S3に置いたログファイルを、ゆるふわなままBigQueryで扱うまでの流れを紹介しました。参考になれば幸いです。

BigQueryは細かい調整をせずとも、大抵の問題をお金とパワーで解決できてしまいますが、スキーマなどどうしても融通がきかないこともあります。
そんなときでも、ちょっとした工夫をしてあげれば便利に使えますし、そういった工夫をしやすくするための機能も豊富で、とても魅力的なサービスだと思います。

fluctでは、そんなゆるふわデータを扱ったり、ゆるふわにならないようしっかりスキーマを定義したりして、一緒に事業を作っていく仲間を募集しています。

株式会社fluct SSP開発本部 ソフトウェアエンジニア | 株式会社VOYAGE GROUP
株式会社fluct エクスペリエンスデザインセンター フロントエンドエンジニア | 株式会社VOYAGE GROUP

そーだいなるVOYAGE GROUPの裏側 #fluct 編 広告配信の舞台裏の技術者たち

f:id:Soudai:20201002183831p:plain

 今日は@soudai1025 こと id:Soudai がお届けします。

 そーだいなるイベントが遂にはじまりました。#voyagebook のイベントとして、各事業部のエンジニアにパネルディスカッション形式で話をしていく企画です。 第一回の今回は「広告配信の舞台裏の技術者たち」と題してfluctのみんなとディスカッションしてきました!

voyagegroup.connpass.com

f:id:Soudai:20201002184005p:plain

当日の紹介資料

 資料の中にfluctの紹介やパネラーの自己紹介もあります。 どんな会社なのか気になる!って人もぜひ資料を見てみてください。

speakerdeck.com

 実際のパネルディスカッションの様子は上記の動画をどうぞ。 和気藹々としながらも噂に違わぬ、fluctの豪腕ぶりには驚嘆しました。

 質疑応答の内容に合わせたツイートなどのまとめはこちら。

togetter.com

感想

 本の中では id:suzu_v さんがフォーカスされてましたが、 id:nishigori-dao さんと id:tomi-ru さんの豪腕っぷりもすごかったですね。 オンプレからECSもすごいし、一人で海外向けの事業を立ち上げ、開発、そして粗利がガンガン出しているのは、まさに今話題の 事業がわかるエンジニア ですね。

 fluctは牧場とか技術選定は無いみたいなパワーワードから、技術的な筋力は普段の業務で伸ばすとかゴールのメリットが見えていれば返済も頑張れるなど金言も沢山ありましたね。 見てない人はぜひYouTubeのアーカイブを見ていただければと思います。

次回予告

 次回のそーだいなるVOYAGE GROUPの裏側は Zucks です! ドキュメントを作らない、フルサイクルエンジニア、本番環境で検証する、など独特な文化と圧倒的なアウトプット力が特徴のチームです。 Zucks回を見た時、「自分がなりたかった理想のエンジニア像はこれだ!」と思う人も多いのではないでしょうか。

 なんと id:Soudai も普段はZucksの中のアフェリエイトチームのお手伝いをしています。 アジャイルだけどスクラムではない、型にハマらないサバンナのようなZucksの話も間違いなく面白いのでぜひ皆さんご参加ください。

voyagegroup.connpass.com

 次回のこの時間もサービスサービス♫ 次回のZucks回もよろしくおねがいします!

techlog.voyagegroup.com

#技育祭 「400人以上の...若手エンジニアが成長するコツ」の動画が公開されたので、口頭で回答したことのいくつかをこちらにも記載しておきます

2020年7月4日、5日の2日間で開催された技育祭 2020では3つのセッションに登壇した @makoga です。

CTOパネルディスカッション

初日のオープニングセッションとなるCTOパネルディスカッションでは、『エンジニアリング組織論への招待』の著者であり株式会社レクター 取締役 広木さん、ビジョナル株式会社 取締役CTO 竹内さん、 合同会社DMM.com CTO 松本さんの3人をパネラーに迎え、私はモデレータとして参加しました。

www.youtube.com

このパネラーで面白くならなかったらモデレータの責任だなと思っていたので、たくさんの人が視聴してくれてホッとしました。

Goライブコーディング

午後には、すずけんのライブコーディングの解説役として参加しました。

※動画が公開されたらここに追記します。

こちらも多くの人が視聴してくれました。ライブコーディングはみんな真剣にみるのでイベント中はすごい静かだけどアンケートの結果をみるといつも満足度が高いイベントなんですよね。

400人以上の...若手エンジニアが成長するコツ

2日目には、『400人以上のインターン生を受け入れ成長させてきたCTOが考える若手エンジニアが成長するコツ』という長いタイトルの単独セッションに登壇しました。

www.youtube.com

このセッションは200人以上の学生が視聴してくれたようです。とても嬉しい。

初日に2つ登壇し、他のセッションを視聴したところ、技育祭に参加する学生はたくさん質問してくれることが分かりました。そこで、最後の質疑応答だけではなく、区切りのいいところで質問ピックアップの時間を2回とりました。

f:id:voyagegroup_tech:20200912104628p:plain
アジェンダ - 若手エンジニアが成長するコツ

これは成功だったと思います。たくさん質問をもらえましたし、視聴者の理解も深まったと思います。

このプレゼンで使った資料は公開していますので、若手エンジニアが成長するコツに興味がある人はぜひ読んでみてください。

speakerdeck.com

このエントリでは、当日にもらった質問やコメントに対して、口頭で回答したことのいくつかを記載しておきます。

当日にもらった質問やコメントと回答

  • 学生:ダニングクルーガー曲線の完全に理解したまでいくのが最初のハードル
    • 回答:自分だけで学べるところまでいくのは簡単ではない。勇気を出して周囲のできる人に支援を求めよう。
  • 学生:筋トレみたい
    • 回答:そうですね。コンフォートゾーンの少し外側で負荷を掛けながら力をつけていくのは似てますね。
  • 学生:研究だけに頼らずいろんなものに手を出すべきか?
    • 回答:好奇心を持って脇目をふらず集中できることがあるならそれでいい。壁にぶち当たっていたり、伸び悩んでいるのであれば、周囲に目を向けることも悪くない。
  • 学生:目標に到達し、次の目標を出す方法は?
    • 回答:一歩引いて視野を広げ目標を探索する目標を立てる。もしくは、到達したものにつながる周辺のことに目を向けてみる。
  • 学生:自分の上位互換みない人がたくさんいる中で競争しなければならない場合、どんなことを意識するといいか?
    • 回答:いきなり同じようになれないし、個性が違うのでなる必要もない。上位互換の人の良いところを1つ選び、それを身に着けるようにする。いろんな人の1つをいくつか身に着けると自分の個性につながる。
  • 学生:目標設定が難しい
    • 回答:繰り返しになるが、他の人が持っているもので自分も身につけたいというものがあればそれを目標にしてみるといい。
  • 学生:分からないことを1人で無限に調べてしまう癖がある
    • 回答:調べることは悪いことではないが、時間的な制約があるなら信頼できる人にすぐ聞くのがいい。その場合、答えを教えてほしいではなく、ここで詰まっているので次の1手を教えてほしいという聞き方がおすすめ。
  • 学生:ただ作るだけなら速いが、凝り性なので時間が掛かる
    • 回答:早い段階で、他の人に見せることを意識するのがおすすめ。
  • 学生:質問の仕方に悩む
    • 回答:分からないことを質問するのもスキルである。ファクトを伝えることを意識しよう。やりたいこと、やったこと、結果を区別して伝えよう。
  • 学生:何か作りたいけど、作りたいものがない
    • 回答:普段使っているツールを模倣したものを作ってみるのがおすすめ。そうすれば仕様を考える時間を最小限にし、技術的なチャレンジに集中できる。
  • 学生:これほど育成に力を入れているのはなぜですか?
    • 回答:ソフトウェア開発力、プロダクト開発力がビジネスの競争力の源泉だと思っているから。

まとめ

技育祭はとても盛り上がりましたし、面白いセッションがたくさんあったと思います。

次の機会があれば #voyagebook 『Engineers in VOYAGE 事業をエンジニアリングする技術者たち』をベースに「事業をエンジニアリングするコツ」の話をしようかなと思っています。

[イベントのお知らせ]

voyagegroup.connpass.com

#voyagebook の書評で頻出する3つのテーマ

こんにちは。技術広報の丹野です。 2020年8月7日に発売された『Engineers in VOYAGE 事業をエンジニアリングする技術者たち』(ハッシュタグ #voyagebook)ですが、その後多くの方々から素敵な感想/書評をいただきました。とてもありがたいですね。今回は本書の書評の中で取り上げていただく機会が多かったテーマについてまとめたいと思います。

f:id:tan2jpn:20200911165048j:plain

頻出する3つのテーマとその書評

生々しい、ここまで公開していいのか

blogやTwitter、Facebookなどでの反響で特に目立つのは、「ここまで赤裸々に公開して大丈夫?」という趣旨のものです。 いずれもVOYAGE GROUPの現役のサービスに関する話題なので、過去に直面した課題のことが話されていても、現時点では解決しているか、解決に向かって進んでいます。そういう観点では安心してお読みください。

​ 将来の状況の変化に伴う課題をあらかじめすべて想定して事業を始めることは、現実的には困難です。 そのなかで、開発者もまた事業の当事者として、システムの技術的な課題だけでなく事業の要請から生まれる課題にも取り組んできた結果が「生々しさ」につながっているのだと思います。 その生々しさがリアリティとして伝わったならうれしいです。 ​

いくつか印象に残った書評を下記に引用させていただきました。

珠玉のエンジニアリング冒険譚 〜 書評「Engineers in VOYAGE 事業をエンジニアリングする技術者たち」 : 小野和俊のブログ

そしてこれらの課題に対して、それぞれのプロジェクトでどんな風に考え、どんな文化を大切にし、どんな苦労に直面しながら、これらを乗り越えてきたのかが記されている。その内容は「企業の名前を入れた本で、ここまで書いていいのか」と思えるほどに赤裸々だ。興味深いのは、テーマの重さと対照的に、課題を乗り越えるためのアイデアはほとんどすべてがしなやかさや遊び心を感じさせる軽やかなものだということだ。

Engineers in VOYAGEを読んだ - Diary over Finite Fields

生々しさゆえ、読んでいても爽快感を覚えることはない。しかし、事業に向きあう開発者を美化せずに描いているし、そして課題解決の過程がとてつもなく面白い。

技術的負債との向き合い方:カッとなってやる短期決戦と腹を括ってやる長期戦

「技術的負債」というキーワードへの反応もたくさん頂戴しています。 インタビュワーの和田さん自身、本書の制作過程であらためてウォード・カニンガム氏による「技術的負債」のもともとの含意を手繰り寄せられたことからも、「技術的負債にどう取り組んでいくか」は本書の大きなテーマのひとつだったと言えるかもしれません。 ​

特に、第3章のVOYAGE MARKETINGや、第5章のサポーターズのエピソードは、数多くの歴戦のWeb開発者の方々の心に響いたようです。 また、第1章のfluctでの「腕力」をめぐる和田さんの話に共感いただいたという声もいくつか拝見しました。 技術的負債との対峙という観点で印象に残った反響を下記に引用させていただきます。

#voyage-book.md · GitHub

fluct の事例では、”技術的負債の返却には腕力が必要” という言葉が登場するのですが、これは非常によくわかるなぁと感じました。こういった過去の辛さを改善していく作業は、ついカッとなってやれる人がいると進んでいくことってあるなぁと振り返ってみても感じます。

一方、ECナビの事例では戦略的に改善していく事例が紹介されていて、これをコツコツやれるのはすごいと感じました。その中で複雑すぎるテーブルの関連性をツールを使って可視化したけど複雑すぎることがわかっただけで有効活用されなかったという話はあるあるで好きでした。複雑なシステムをツールなどを使って可視化してみたけど、どうすればいいのかわからずそこで満足して終わってしまうことはよくありますが、そこから別のアプローチで進めていったのはとてもいい事例だなと感じました。

事業会社の現場 - 「Engineers in VOYAGE ― 事業をエンジニアリングする技術者たち」 - Shin x Blog

3 章は、それ以外にも、データベースのテーブル数を 1200 から 450 まで減らしたり、システムをスリム化していく流れでコード、機能、事業の削減(葬り)に分けて実施していくという骨太の内容でした。これを 5 年かけて通常業務と並行しながら除々に実施したというのは、大変ではありますが、粘り強く少しづつでも進めることで技術的負債を返却できるという一つの指針を見ることができました。

Engineers in VOYAGE - 事業をエンジニアリングする技術者たち - を読んだ

単に技術的な機能面だけはなく、事業ごと葬る、といったようにビジネスサイドとも合意を得て力強く進められていることがわかる。 このような経験・スキルは中で体験しているエンジニアでないと獲得しがたいものである。もちろん、書籍から得られる知見は表面上に限るかもしれないが、その内容について少しでも触れることができるという意味で、大変貴重な書籍である。

事業を作る人達の物語が最高だった #voyagebook - そーだいなるらくがき帳

要所でそれぞれの事業部がそれぞれの技術的負債に対して、どのように解決してきたかが書いてある。 そのアプローチは事業のフェーズ、規模によっても当然違ってくるが、それが各事業毎に違ったアプローチで書かれているのだが、それがとても参考になる。 リプレースなのか、リアーキテクチャなのか、リファクタリングなのか、それぞれ全く違うアプローチを1冊でそれぞれ見れる貴重な本だ。 今まさに技術的負債に立ち向かおうとしているのであれば一度は読んだ上で、アプローチの参考にしてほしい。

本:Engineers in VOYAGE - 事業をエンジニアリングする技術者たち|hidenorigoto|note

本書を読んで私が思うのは、VOYAGE GROUPは、負債の上手い乗りこなし方を身に付けているということです。事業開発という目的のためには、避けて通れない技術的負債の問題に対して、組織としての対応能力を磨いているように思います。

事業をエンジニアリングする

本書のサブタイトルにもある「事業をエンジニアリングする」という観点への反響もたくさん頂いています。 当初は『事業をエンジニアリングする技術者たち』のほうが本書のメインタイトルだと思われてしまう誤算もありましたが、 これはカバーの印象だけからくる勘違いではなく、やはり本書を実際に読んで感じて頂いたものが「事業をエンジニアリングする」というフレーズにとても合致していたからこそだと思います。 ​

頂いた反響のなかにも、下記に引用させていただいたもののように、このフレーズに込めたメッセージを的確に読み取っていただけたと感じられるものがたくさんありました。

珠玉のエンジニアリング冒険譚 〜 書評「Engineers in VOYAGE 事業をエンジニアリングする技術者たち」 : 小野和俊のブログ

正論だけではまかりとおらない、事業という複雑で壮大な冒険をVOYAGEのエンジニアたちはどんな風にして生き抜いてきたのか。 本書のサブタイトルは「事業をエンジニアリングする技術者たち」。 そう。ここで登場するエンジニアたちは、ソフトウェアだけでなく、事業そのものをエンジニアリングしているのだ。

「Engineers in VOYAGE ― 事業をエンジニアリングする技術者たち」を読んだ #voyagebook | by Yuki Fujisaki / tnj | Aug, 2020 | Medium

この本の中では、事業方針や組織体制と、開発して生まれるソフトウェアの設計は切っても切り離せない、コンウェイの法則的な話が何度か登場します。また、出てくるチームは大きさもバラバラで、文化もそれぞれ異なりっているのですが、事業としてのエンジニアリングを全員が認識しているのが自分の中では印象的で、フルスタックエンジニアではなくフルサイクルエンジニアの考え方を軸に置いているのが組織文化に繋がっているように感じました。

事業を作る人達の物語が最高だった #voyagebook - そーだいなるらくがき帳

5年、10年、15年モノの事業との付き合い方や0から始める新規事業との付き合い方、そしてそれぞれのフェーズで新しいことにチャレンジしていくために如何にビジネスを犠牲にせずに立ち向かっていくか。 そんな物語の中に哲学がある。

「事業をエンジニアリングする技術者たち Engineers in VOYAGE」を読みました

エンジニアが勝手に考えた「キレイなシステム」とか「最強のシステム」ではなく、ビジネスと照らし合わせた上で解決すべき課題を定義して改善するという進め方が全体としてあるように感じました。

#voyage-book.md · GitHub

エンジニアリングは事業のために行われるものであり、本書では様々なシステムや様々な状況の話が出てくるのですが、事業のためにという点が全ての共通点としてブレてない点に企業としての強さを感じました。この点については技術力評価会に参加している中でも感じたことだったので、本書を読んだ改めて納得した部分です。

事業会社の現場 - 「Engineers in VOYAGE ― 事業をエンジニアリングする技術者たち」 - Shin x Blog

インタビュイーである開発現場のエンジニアの方々が圧倒的な当事者意識を持って仕事に取り組まれているということです。これはシステムに対してだけでなく、事業に対しても同様です。事業会社で働くなら当たり前のことかもしれませんが、システム、さらにその目的である事業をいかに自分のこととして捉えられるかによって考え方も起こすアクションも変わってきます。

事業会社の現場 - 「Engineers in VOYAGE ― 事業をエンジニアリングする技術者たち」 - Shin x Blog

こうした当事者意識は本人の心持ちだけではなく、それを後押しする環境、文化にも影響を受けます。もし、自分がこうした意識を持とうとしても、すぐにボールを取り上げられる環境や事業サイドの下請けのような立場になっている環境ではそれを継続することは難しくなります。本書で登場する 5 社では現場のエンジニアが当事者意識を持てるような、後押しするような環境になっているのだろうということを感じました。

本:Engineers in VOYAGE - 事業をエンジニアリングする技術者たち|hidenorigoto|note

ソフトウェアエンジニアのモチベーションの源泉というのは人によって様々ですが、「事業に貢献する」ことに強くモチベートされる方が、偶然私の周りには多くいます。「Engineers in VOYAGE - 事業をエンジニアリングする技術者たち」は、そういう種類のソフトウェアエンジニアが読むと、この会社で仕事したら楽しいだろうな!と思わせてくれる本だと思います。

書籍『Engineers in VOYAGE 事業をエンジニアリングする技術者たち』とは

本ブログによる発売エントリ

書籍「Engineers in VOYAGE 事業をエンジニアリングする技術者たち」が発売 #voyagebook - VOYAGE

  • 本書内の「はじめに」を全文掲載しています。

本書の編者:和田さん (@t_wada)のブログエントリ

『Engineers in VOYAGE ― 事業をエンジニアリングする技術者たち』ができるまで #voyagebook

  • 「はじめに」のはじめに、本書のきっかけ、インタビュー準備と本番、各章の読みどころ、本書へのさまざまな方からの感想などをまとめていただいています。

本書の編集者:鹿野さん (@golden_lucky) のブログエントリ

2010年代に日本のインターネットでいろんな事業をいい感じにやってきた会社から2020年代へのヒントをもらえる本を作った

  • なぜ VOYAGE GROUP?なぜ t_wada?なぜ宇宙船?で、結局のところどういう本なの?など、鹿野さんの視点から本書を振り返っていただいたアツいブログエントリ。

本書の購入について

最後に

VOYAGE GROUPのエンジニアたちやエンジニアリングの文化に興味を持っていただけた方は、本書のインタビュイーたちともカジュアルに質問や情報交換などができる機会を設定したいと思いますので、以下のリンクからご応募いただけると幸いです。

「Ask the Engineers in VOYAGE」

エンジニア向けサマーインターンシップ Treasure2020が終了しました!

こんにちは!VOYAGE GROUPの中田です。 Treasure2020ではサポーターをやってました!

8月1日より開催した『Treasure』が、8月28日に終了しました! 興奮が冷めないうちに、その様子をお届けしたいと思います。

目次

Treasureとは

2006年から毎年夏に開催しているエンジニア学生向けのインターンシップ。今年でもう15回目になります!

TreasureはVOYAGE GROUPのものづくりのエッセンスが詰まっており、それを現場のエンジニアから直接感じてもらえるようにデザインしています。

VOYAGE GROUPのエンジニアが普段やっていることや考えていることを講義で事前に伝え、それを元にチーム開発することで、チームでもの創りをする難しさや楽しさを感じてもらう内容にしています。

学生が1人では学びにくい「価値を生み出す考え方」「継続的にサービスを支える考え方」「チームで実践する考え方」を伝え、しっかりと学習と実践のサイクルを回していく場にしています。

Treasureのテーマは「価値のあるものづくり」と「Goを使って学ぶソフトウェアエンジニアリングとチーム開発」。 今年は8月1・8・15・17日を講義、18〜28日をチーム開発としました。

講師・サポーター陣がフルコミット!

Treasureの特徴は参加学生よりも講師やサポーターが多いこと。 今年は参加学生24人に対して講師・サポーターは32人でした!

Treasureでは、1人だと学びにくいことを学ぶ場として、学生が疑問に思ったことはすぐに講師やサポーターに聞ける環境にこだわっています。

前半の講義では講師や内定者TAが、後半のチーム開発では1チーム学生4人につきクルーが3~4人サポーターとしてフルコミットしました。

初のフルリモート開催

さて歴史あるTreasureですが、今年は世間の状況もあり、初めて全日リモートで開催しました。

元々、Treasureは今年の1月下旬には日程を告知しており、3月時点ですでにたくさんの学生の応募がありました。しかしCOVID-19の影響で学生の夏休みが変更になり、当初予定していた日程では開催しても学生が集まるか怪しく、オフラインで開催できる見通しも立たない状況でした。ただ、「Treasureを開催する!」という意思は変わらなかったので、オンライン前提で開催できるように意思決定をしました。

とはいえ、初のオンライン開催。コミュニケーションの取りづらさにより進行がスムーズに行かないのではないか、フォローが十分行き渡らないのではないか、熱気が作りづらいのではないか、などの懸念がありましたが、できる限りの対策を準備し、当日を迎えました。

f:id:carter_vg:20200907143254j:plain
機器もセットし、準備完了

参加学生は自宅からTreasureに参加。北海道から九州まで、全国各地の学生さんが参加してくれました。

f:id:carter_vg:20200907144058j:plain
気合いの入る一言でスタートします

前半:講義

講義内容

  • GoでWebアプリケーション開発
  • フロントエンド(JavaScriptの基礎でevent loopからWebRTC,Reactなど)
  • データベース設計・データベースモデリング
  • インフラ
  • アイデアソン

オンラインという特性上、例年と比べ個別での質問対応やフォローアップがやりづらい環境のため、参加学生の理解度に差が出てしまわないかという懸念がありました。

そこで、下記を実施しました。

  • インタラクティブになるように、講師が全体に質問を投げかけ、直接答えたりSlack上で回答してもらう
  • Zoomのブレイクアウトルームに分け、学生3人につき1人は講師か内定者TAがつき、その場で質問に答えられるようにする
  • 悩んでそうな学生へ内定者TAがDMで連絡をし、フォローアップする
  • 講義中に理解できなかった部分は後日講師が解説する補講日を作った
  • 講義や補講を録画し、自分でわからなかったところを後日、復習できるようにした

後日参加学生に取ったアンケートには、

  • DMで困ったことがないかときどき聞きに来てくれるのがとても助かった
  • わからないところをマンツーマンで教えてくれたのが良かった
  • Slackで質問しても丁寧に返信をくれたのが嬉しかった

というコメントをもらうことができました。講師や内定者TAのサポートのおかげで、講義の理解を深めていくことができたようです。

f:id:carter_vg:20200907150036p:plain
講義の様子
f:id:carter_vg:20200907144207j:plain
裏側ではこんな感じに

後半:チーム開発

講義で学んだことを基に、4人1組に分かれチーム開発を行いました。 社会構成や価値観の変化(=時流)に伴い生まれてきた課題や欲求を満たすものを開発しよう、という内容のものでした。

各チームは学生4人に、エンジニア2名がサポーターとしてフルコミット。まずはどのような時流があるかを各チームで議論し、価値のあるプロダクトのアイデアを固めていきました。 普段はホワイトボードや模造紙を用いて行うワークですが、今回はMiroというツールを使い、オンライン上でのアイデア出しや思考整理を行いました。

f:id:carter_vg:20200907144413j:plain
Miroでアイデアワーク

アイデアが固まったら開発。チーム開発においても、コミュニケーションの取りづらさから、議論や開発がスムーズに行きづらい懸念がありましたが、常にZoom等のWeb会議ツールを接続し声をかけあえる状態にしたり、画面を共有し合いながら開発をしていきました。

f:id:carter_vg:20200907144443j:plain
開発の様子

そして、随時サポーターとの個別面談を積極的に行い、個々人の不安を解消していきました。最初はぎこちなさもありましたが、日を追うごとにコミュニケーションが円滑に行え、例年と変わらないチームの結束が出来上がっていきました。

またチームでの取り組み以外にも、全体での朝会や夕会で進捗の共有や決意表明。時にはお誕生日会やajitingなどのイベントを設け、全員での共通イベントを開催しました。 *1

Treasureといえば、「同世代のイケてるエンジニアと仲良くなれる!」という口コミが広がっていますが、オンラインでどう作っていくのか試行錯誤しました。 しかし、上記のような全体で顔を合わせる機会を作ることで、オンラインでも「24人でTreasure生である!」という思いをみんな持てたのでは?と思っています!

f:id:carter_vg:20200907150135p:plain
サプライズで誕生日会を開催

最終発表

10日間の開発期間も終わり、いよいよ出来上がったものを披露する最終発表!発表の仕方も各々のチームの色が出ていました。

f:id:carter_vg:20200907144808p:plain f:id:carter_vg:20200907144818j:plain f:id:carter_vg:20200907145256p:plain f:id:carter_vg:20200907145308p:plain f:id:carter_vg:20200907145324p:plain f:id:carter_vg:20200907145335j:plain

発表の後はブースタイムを設け、講師の方を中心にさらに個別での質問を受けました。

最後に審査員による賞の発表。 「API設計賞」「UI/UX賞」「データモデリング賞」「DevOps賞」「ニーズ賞」「グランプリ」に分かれ、表彰されました。

そして最後にみんなで打ち上げを行い、Treasureは幕を閉じました!

f:id:carter_vg:20200907145453j:plain
打ち上げの様子。Treasureおつかれさまでした!

参加学生の感想

  • サポーターさんのサポートが手厚いという評判は聞いていましたが、想像以上にサポートしてくれてとてもありがたかったです。講義の日では1on1でコードをみてもらったり、チーム開発ではとてもいい助言やエラー解消の方法を的確に教えていただけました
  • webの面白さはもちろんですが、それ以上にプロダクトづくりの面白さを肌で実感することができました!自分の作りたいもの・価値あるものを作れるように今後も技術に貪欲にいたいと思いました!
  • 振り返ると本当に意味が分からないほど参加者を育てるための仕組みみたいなものがたくさん用意されていて、VOYAGE GROUPは良い環境なのだろうなということを思いました
  • オンラインだと熱が入るのかな、あるいは初対面のメンバーと打ち解けて開発できるのかなと不安でしたが、全然そんなことはなく、最高に熱い夏になりました!!!めちゃくちゃ楽しかったし、めちゃくちゃ勉強になりました!
  • ここまで本気で駆け抜けたのはTreasureが初めてでした。本当にお世話になりました

感想ブログ

早速、Treasure2020の感想ブログを書いてくださった方も。ありがとうございます!

hirocky86.hatenablog.com

ogijunchang.hatenablog.com

Treasureに参加した話 byたくりんとん

https://blog.takurinton.com/17

note.com

tokoroten-lab.hatenablog.com

kudoa.hatenablog.com

最後に

オンライン開催ということで、不安を抱えながら開催した部分もありますが、多くのクルーに協力をいただきながら創り上げることができ、終わってみれば例年と変わらない熱気を生み出すことができました。

私も久しぶりのサポーターとしての参加でしたが、より良いプロダクトを作れるようどうサポートするか、参加学生の成長をどう支援していくかを、立場や職種関係なく関わっている全員が本気で考え取り組むから、この満足度を生むことができるのだなと感じました。 本気で向き合ってくれる人が多いところがVOYAGE GROUPの良さだなと感じますし、参加学生の皆さんには、ぜひ得たものをさらに自らの手で磨いていき、これからも成長していってもらいたいなと思います。

次回のTreasureは来年になると思います。 参加資格を持つ学生さん、ぜひご応募お待ちしております!

*1:ajiting…VOYAGE GROUPの社内バーAJITOでお酒などの飲み物とPCを片手に語り合う文化。今回はオンラインの仮想AJITOを設け、終わってからちょっと集まって雑談をしました。

書籍「Engineers in VOYAGE 事業をエンジニアリングする技術者たち」が発売 #voyagebook

こんにちは。技術広報の丹野です。 2020年8月7日、『Engineers in VOYAGE 事業をエンジニアリングする技術者たち』という本が ラムダノートさん から出版されます。

テスト駆動開発でもおなじみの 和田(@t_wada)さん が、VOYAGE GROUPに在籍する主要なソフトウェアエンジニアにインタビューし、その内容をラムダノートの 鹿野(@golden_lucky)さん の協力のもと本としてまとめていただきました。 VOYAGE GROUPにおけるビジネスとソフトウェア開発の在り方を濃縮した1冊に仕上がっていると思います。

f:id:tan2jpn:20200806173146j:plain
Engineers in VOYAGE 事業をエンジニアリングする技術者たち 書影

さて今回は、この本ができた経緯や内容、雰囲気についてみなさんに伝えたいと思い、 和田さん と ラムダノートさん の許可を得て「はじめに」の内容を以下に掲載します。ツイートする際には、ハッシュタグ #voyagebook を使っていただけると嬉しいです。

author: 和田卓人 date: 2020年7月

はじめに

 今世紀に入ってから、ビジネスとITの関係は大きく変わりました。Marc Andreessenが"Why Software Is Eating The World"というタイトルの文章をウォールストリートジャーナルに寄稿してから9年が経ちます。世界を飲み込むソフトウェア化の波はインターネット企業から始まり、ソフトウェアから遠い伝統的産業にまで到達しました。新興企業はソフトウェアの力を活かして伝統的産業に参入し、既存の企業は新たな好敵手に対抗するためソフトウェアの力を獲得しようともがいています。ビジネスにとってITは、「あると便利」から「有効」、「不可欠」を経て「中核そのもの」になりつつあり、ソフトウェアのエンジニアリングを進める力は企業の競争力の源泉となりました。

 1999年創業のVOYAGE GROUPは、ソフトウェアの力を活かして成長してきたインターネット領域の事業開発企業です。20年間で100以上の事業やサービスを創出し、現在は広告プラットフォーム事業やメディア事業を中心に20以上の事業やサービスを運営しています。本書は、そのようなVOYAGE GROUPのさまざまな事業やサービスを牽引するエンジニアに2020年1月から2020年5月にかけて行ったインタビューを1冊の書籍としてまとめたものです。事業をエンジニアリングの視点で考えたり、さらにはエンジニアリングの視点で事業を作り出したりと、「ビジネス」と「エンジニアリング」を両輪にしてITシステムを開発・運用しながら実社会で活躍している技術者や技術者のチームの生の声をお届けします。

本書のきっかけ

 本書の企画は、もともと「VOYAGE GROUPのエンジニアをもっと多くの人に知ってもらいたい」というCTO小賀さんの想いから始まりました。同社のいくつかの事業会社のシステムを支えるエンジニアに取材した内容をインタビューとしてまとめ、それを技術同人誌のような形で頒布することが意図されていたようです。

  筆者は、同社の技術コーチを務めている関係から、この企画のインタビュアーを拝命しました。各事業会社のキーパーソンにインタビューを依頼し、ランチをともにしながら事前に聞き出したshow notesを手に本番に臨むと、そこで彼らが語ってくれたのは、現実の世界で次々に発生する問題やチャレンジングな目標を時には腕力、時には調整力、時には洞察力でもって解決していく、当事者意識と技術力を備えた技術者たちによる格闘の歴史でした。

 インタビューの内容を文章として書き起こす作業は、編集者である鹿野さんが引き受けてくれました。そうして生まれた文章は、一企業の技術者の認知度向上という当初の目標を遥かに超え、「事業をエンジニアリングしていく」というITシステムが事業の中核になった現代において普遍的なテーマについて当事者が語る貴重な証言を集めた内容になっていました。これは良い本になるという手応えを掴みました。「この本は多くの方にヒントを与える本であり、広く読まれるべきである」との想いから、鹿野さんの出版社であるラムダノートで書籍として発行するに至ったものです。

おすすめの読み方

 本書はさまざまな読み方ができる本です。すでに触れたように、本書のもともとの意図は「VOYAGE GROUPの名前は知っているものの、どんなエンジニアがどんなシステムを作っているかまでは知らなかった」という方に興味を持って手に取って読んでもらうことでした。もちろん、すでにVOYAGE GROUPに知り合いがいる方にも、一種のファンブック的に楽しんでもらえるでしょう。

 しかし筆者は、本書はそれ以上のものであると考えています。なぜなら、本書に登場するVOYAGE GROUPの複数のシステムには多くの方が「自分の扱っているシステムが置かれている状況に似ているな」と思えるような面がどこかにあり、したがって本書には「自分事としてとらえられる切り口」が必ずどこかに見つかるはずだからです。

 たとえば、大量のトラフィックをさばくためにエラスティックにスケールアウトしていく配信システムもあれば、複雑な情報を扱う管理画面もあります。オンプレミスで始まったシステムもあれば、最初からクラウドコンピューティング基盤を活用したシステムもあり、さらに最初からクラウドネイティブなマネージドサービスを使っているシステムもあります。散らかったデータの収集と前処理から着手して、現在ではデータサイエンスの力を活かしてリアルタイムの予測を導入しているシステムもあります。BtoBのシステムもBtoCのシステムもあります。ゼロから立ち上げて数年の若いシステムもあれば、20年もののレガシーシステムもあります。

 システムは事業の種類によっていろいろと姿形を変えます。そしてVOYAGE GROUPという会社では、さまざまなフェーズにある多様な事業を扱っており、強いエンジニアリング文化がそれを支えています。 したがって本書のどこかのページには、システムに携わっている方の多くにとってヒントとなる(あるいはアンチパターンとなる)逸話がきっとあるはずなのです。

 インターネット上で事業を営んでいる企業に属しているエンジニアの方々は、自社がこれまでに経験したことやこれから経験しそうなことについて、いろいろなステージの事業が登場する本書からヒントを見つけられるでしょう。アーキテクチャの変遷や技術的負債の返済の道のりなど、エンジニアリングの力によって問題を解決していくさまに共感しながら読めるのではないでしょうか。

 受託開発を行っているエンジニアの方々におすすめしたい本書のポイントは、インターネット企業のエンジニアたちの等身大の姿、事業に対する当事者意識、ビジネス上および技術上の意思決定のスピード感などを具体的に読める点だと考えます。それだけでなく、たとえば第3章のVOYAGE MARKETINGへのインタビューに顕著ですが、「SIの現場で培ったスキルのウェブ系のサービスでの活かし方」といった観点でも参考になると思います。

 さらに本書は、技術者だけでなく、ビジネスを営んでいる方々にもおすすめできます。それは、全編を通して「オーナーシップを持った技術者が事業をエンジニアリングする姿」を見られるからです。ビジネスとエンジニアリングが事業の両輪であるからには、エンジニアたちの考えを知り、彼らの気持ちを汲み取れることは、ビジネスにとって必須のスキルだといえるでしょう。

 特定の企業のエンジニアリング文化を解説する書籍はこれまでもありましたが、それらに登場するのはGoogle、Amazon、Facebook、Appleなど、技術者の数も多ければエンジニアリング的な体力もふんだんにあるメガプレーヤーたちです。VOYAGE GROUPは、それらメガプレーヤー企業に比べると遥かに規模が小さいにもかかわらず、強くしなやかなエンジニアリング文化を備え自分たちで問題を解決しながら日々前進している企業だと思います。その意味では、「あれらの企業はそもそも特別すぎるから」という先入観から離れ、より現実的で模倣可能なエンジニアリング文化を本書から読み取ってもらえるはずです。

書誌情報

書名: Engineers in VOYAGE 事業をエンジニアリングする技術者たち
著者: 株式会社VOYAGE GROUP 監修、和田卓人 編
サイズ: A5判、224ページ
ISBN: 978-4-908686-09-2
本体価格: 1,800 (+税) 円、電子版のみ1,000 (+税) 円
発行: 2020年8月7日

主要目次

第1章 fluct:広告配信の舞台裏の技術者たち
第2章 Zucks:フルサイクル開発者の文化
第3章 VOYAGE MARKETING:20年級大規模レガシーシステムとの戦い
第4章 VOYAGE Lighthouse Studio:数十万記事のメディアをゼロから立ち上げる
第5章 サポーターズ:事業の成長を止めない手段としてのシステム刷新
第6章 データサイエンス:エンジニアによるビジネスのための機械学習

購入について

ラムダノートさん で販売しています。

そのほか、ジュンク堂書店池袋本店をはじめ、書泉ブックタワー (秋葉原)、紀伊國屋書店新宿本店、amazon.co.jp などの書店およびオンライン書店でも順次販売予定となっています。

最後に

この本を通して、インタビュイーを務めてくれたエンジニアたち、そしてVOYAGE GROUPの文化や考え方について共感していただけたら幸いです。

FluctSDKのドキュメントをリニューアルした話

こんにちは。新卒2年目エンジニアのchocovayashiです。 普段は、fluctでiOS / Android / Unity向けに広告SDKの開発をしています。

広告SDKはSDK自体の機能開発も大事ですが、使い方をわかりやすく説明するためのドキュメントの存在も大事です。 今回はFluctSDKのドキュメントを新しく作り変えたので、そのアプローチやどういうツールを使ったのかご紹介します。

新しいドキュメントはこちらです。

https://voyagegroup.github.io/FluctSDK-Doc

f:id:chocovayashi:20200716110520p:plain

目次

ドキュメントが抱える課題

作り変える前のドキュメントは、各プラットフォーム(iOS, Android, Unity)ごとにGitHubのWikiで管理していました。

iOS, Android, Unity (Wikiでの記述は既に消しているので、現在はHomeだけしか見れません。リンクは過去の編集履歴へのリンクです。)

1. 商材が増え、見たいドキュメントに辿り着くことが面倒になった

iOSの例を挙げると、私が入社した1年ほど前は、動画リワード広告JavaScriptタグによるバナー広告しか商材がなく、見たいドキュメントをすぐ見つけることができました。しかし、ここ1年で商材が増え全て画像のように箇条書きにしていたので、見たいドキュメントを探し出すのに苦労するようになりました。画像はiOSの商材で、iOSだけでも画像の倍の商材があり、またAndroid, Unityにもほぼ同じ数の商材がありました。

f:id:chocovayashi:20200716110331p:plain

視覚的に商材を選べるUIにできれば簡単にたどり着けるようになりそうだなと思いました。

2. 同じ記述になる部分を重複して管理している

FluctSDKのUnity Pluginの実態は、iOS, Androidの各プラットフォームへのbridge機能を有しているだけで、ロジック部分はiOS, AndroidのSDKがそれぞれ有しています。 なので、FluctSDKのUnity PluginにおけるiOSやAndroidの最低要求OSバージョンは、FluctSDKの要求するiOS,Androidの最低要求OSバージョンと同じになります。 各プラットフォームごとにドキュメントを公開している(内部的にも各プラットフォームごとにドキュメントを管理していました)制約上、各プラットフォームを跨いで情報を共有できなかったので、共通の情報であっても重複した記述をする必要がありました。その影響で、変更漏れが発生することがありました。

3. おしゃれじゃない

個人的にはこれが最も重大な課題でした。デザイナーではないのでおしゃれの細かい定義はしていませんが、左に目次があったり、複数の言語の実装例をタブで選択できるようにしたいなと思っていました。

他に意識したところ

  • gitなどでバージョン管理ができる
    • チーム内でのレビューをしやすくしたい
    • 変更履歴がわかるようにしたい
  • ドキュメントの構成をツールに依存させない
    • ツールのサポート終了などで将来他のツールに移行する可能性は十分にあるので、ドキュメントの書き方がツールに依存しすぎないようにする

ツールの選定

まずは選択肢を挙げ、それぞれについて考えました。

GitBook

f:id:chocovayashi:20200716110420p:plain

トップページから溢れ出るおしゃれ感には心を揺さぶられました。また、Analyticsも見れるのでとても魅力的でした。ただ、localでの開発が非推奨であったり(https://github.com/GitbookIO/gitbook )、お金がかかるというのはデメリットだと考えました。

HUGO

f:id:chocovayashi:20200716110440p:plain

HUGOはタイトルからもわかるように、staticなsiteを作れるオープンソースのframeworkです。テーマが多彩でその1つにドキュメントっぽいテーマもあったので十分活用できそうでした。また、hot-reloadもあるので、快適に開発できそうだなと印象を受けました。

中身はgoで書かれているので、デプロイする時は例えばCIでhugoコマンドを使い、MarkdownをHTMLへ変換し、その HTMLをデプロイする必要がありました。またドキュメントの公開という観点からすると、少しオーバースペックな気がしました。

docsify

f:id:chocovayashi:20200716110502p:plain

まず見た目がとても可愛いので良いですね。docsifyはJavaScriptで書かれたドキュメント生成ツールです。静的ファイルとして置いたMarkdownをdocsifyが表示時にHTMLへ変換するので、build不要で公開できるのが魅力的でした。HUGOと同様にhot-reloadもあり開発には困ることはなさそうです。また、pluginの開発もできるので、独自拡張もしやすそうでした。

ただ、テーマが4種類しかないので、もし凝ったデザインを作りたくなったら0から考えないといけないのが大変だなと思いました。

ツールの選定結果

候補の中から、docsifyを採用することにしました。docsifyはCover pageという機能があり、トップページをHTMLで自由にカスタマイズすることができます。今回は見たい商材へすぐに辿り着けるようなデザインへとカスタマイズしました。

f:id:chocovayashi:20200716110520p:plain

ドキュメントの埋め込み機能がありコンテンツを再利用できるので、重複する情報の管理を1箇所で行うことができるようになりました。例えば、iOSの最低要求OSバージョンをiOSのドキュメントに書いていてもそれをFluctSDK Unity Pluginで再利用することが可能になりました。

デザインの面では、Tab機能で見た目を簡単にリッチにできる点も採用の決め手になりました。

f:id:chocovayashi:20200716110542p:plain

docsifyを使う上でちょっとした工夫

SDK本体のリリースサイクルの弊害にならないようにしたい

ドキュメント内にSDKのversionを記述してる箇所があり、それはSDK本体のリリース毎に修正する必要がありました。これを手動で毎回変更する運用にすると、変更を忘れたり、変更をするのが面倒で細かくリリースを行う弊害になりかねませんでした。なので、GitHub Actionを使い自動でversion upする仕組みを作りました。

GitHub Actionには特定のリポジトリにjsonの情報付きで通知を送る仕組みがあるので、それを利用しSDK本体がリリースされるタイミングで、ドキュメントを更新する仕組みを作りました。

ただ、version情報は各所に点在していて、GitHub Actionがその全ての場所を修正しようとすると、ドキュメントの可用性が低下してしまいます。なので、version情報などをyamlファイルで一元管理できるpluginの独自開発を行いました。plugin自体はMarkdownの埋め込みとほぼ同じ実装かつ後述するので、ここでは省略します。

低コストでデプロイを自動化する

ドキュメントの中身以外のメンテナンスは極力したくないのでCIは使いたくありませんでした。というのも、実際に価値を生むのはSDK本体なのでそこの開発の邪魔にならない立ち位置で運用したかったからです。またドキュメントの性質上、大量の同時アクセスなどを考慮する必要もありませんでした。なので、今回はGitHub Pagesを採用しました。さらに、buildする必要もないので、masterへmergeするだけでデプロイすることが可能になりました。

GitHub Pagesをホスティング先にすることにより、CIを使わずにデプロイすることが実現できました。

docsifyを使う上でちょっと困ったこと

Tab機能とドキュメントの埋め込み機能を併用で使えない

docsifyはMarkdownをHTMLに変換してドキュメントを表示しています。ドキュメントの埋め込み機能はHTMLへ変換する前に別のMarkdownを読み込み、展開させる機能です。一方でTab機能はドキュメントの埋め込み機能がMarkdownの展開をする前に処理が行われており、2つの機能を共存させることができませんでした。

なので、Tab機能とドキュメントの埋め込み機能を共存させるために、ドキュメントの埋め込み機能をpluginとして独自で開発し、タイミングを制御できるようにしました。簡単にpluginが作れるところもdocsifyの良いところですね。

まずはdocsifyにPluginを登録します。

window.$docsify.plugins = [].concat(
  embedMarkDownPlugin,
  window.$docsify.plugins || []
);

window.$docsify.pluginsへpluginを追加します。embedMarkDownPluginはpluginの中身です。

function embedMarkDownPlugin(hook, vm) {
  hook.beforeEach(function (content, next) {
    embedMarkdown(content, next);
  });
}

hook.beforeEachはdocsifyで定義されたライフサイクルのコールバックです。他にも様々なコールバックが用意されています。 embedMardownはMarkdownを埋め込む実態の関数です。

function embedMarkdown (content, next) {
  const regex = '\\[embedded-markdown\\]\\(([a-zA-Z0-9!-/:-@¥[-`{-~]+)\\)';
  const embeddedDescriptions = content.match (new RegExp (regex, 'gim'));
  if (!embeddedDescriptions) {
    next (content);
    return;
  }
  const embeddedFiles = embeddedDescriptions.map (desc => {
    return desc.match (new RegExp (regex, 'im'))[1];
  });

  Promise.resolve ()
    .then (function () {
      return Promise.all (
        embeddedFiles.map (desc => {
          return fetch (desc).then (res => (res.ok ? res.text () : ''));
        })
      );
    })
    .then (responses => {
      responses.forEach ((res, index) => {
        const escapedEmbeddedDescription = embeddedDescriptions[index].replace (
          /[\\^$.*+?()[\]{}|]/g,
          '\\$&'
        );
        const regex = new RegExp (escapedEmbeddedDescription);

        // 指定されたmarkdownが存在しない場合
        if (!res) {
          content = content.replace (regex, '');
          return;
        }

        // markdownを入れ替え
        content = content.replace (regex, res);
      });
      embedMarkdown (content, next);
    });
}

このように定義し、Markdownに以下のような記述をすると、指定したMarkdownを埋め込むことが可能になりました。

[embedded-markdown](path/want-to-embed-markdown.md)

まとめ

いくつかのツールを検討して、最初に挙げた課題を最短で解決してくれそうなdocsifyを採用しました。

広告SDKの開発においては、どうしても広告の機能開発に目を向けがちですが、お客さん目線に立つとドキュメントの見易さも非常に重要になってきます。地味な作り変えかもしれませんが、少しでもお客さんの開発の助けになればなと思います。