静的解析とUIの自動生成を駆使して
モバイルアプリの運用コストを
大幅に下げた話
The Go gopher was designed by Renee French.
The gopher stickers was made by Takuya Ueda.
Licensed under the Creative Commons 3.0
Attributions license. 1
2017/08/05(土)
@Builderscon Tokyo 2017
自己紹介
メルカリ/ソウゾウ
上田拓也
twitter: @tenntenn
■ コミュニティ活動
Google Cloud Platform User Group (GCPUG) Tokyo
Goビギナーズ
golang.tokyo
Go Conference
■ 業務
GAE/Goでメルカリカウルを作ってます
GoやGCPコミュニティを盛り上げる仕事
Gopherを描く仕事(LINEスタンプ)
2
アジェンダ
● モバイルアプリとコンテンツ管理
● バナーツール開発の背景と実践
● 汎用性の維持と機能拡張
● [バナーツールを支える技術]
3
モバイルアプリとコンテンツ管理
4
モバイルアプリとコンテンツ管理
5
■ 多様なコンテンツを扱う
● アプリ内バナー
● ゲーム内ショップ
● イベントやキャンペーン
● 全体お知らせ
■ コンテンツの特徴
● 数が多い
● 更新頻度が高い
● 配信箇所が複数
● 配信期間がある
● 表示の優先度がある
● ユーザごとに出し分けが必要
○ OSやユーザの状態による
メルカリアッテの例
■ アプリ内バナー
● タイムラインにバナーを表示させている
● バナーをタップするとWebViewなどに遷移
● タブごとやユーザの状態によってバナーが変わる
6
バナーツール
■ バナーの入稿を管理するツール
● 式で配信条件を設定
● 1ソースで複数の環境に対応
7
バナーツール
・配信条件
・レスポンス
バナー取得
OS, APIバージョン などの変数
条件に当てはまるバナー
画像URL, 遷移先URL
アプリ
バナーツールの特徴と使われどころ
■ 複数のアプリで使える
● メルカリ、メルカリ アッテ、メルカリ カウル
● 個人環境も簡単につくれる
■ コンテンツ管理全般に使える
● バナーの管理
● 全体お知らせの管理
● その他、変化の激しいコンテンツの管理
8
デモ1:バナーの設定と配信
9
https://youtu.be/L61IaI-zqyk
バナーツール開発の背景と実践
10
これまでのバナー管理の問題点
■ PHPやTOMLファイルで管理されていた
● 実装は簡単だが運用が大変
● バナーを設定する度にエンジニアが必要
● どの設定が使われているのか分からない
11
[[banners]]
tab_id=2
url=https://example.com/campaign?id=10
image_url="https://example.com/campaing10.png
start_time=2016-10-27T08:00:00Z
end_time=2016-11-07T06:00:00Z
os="ios"
バナー設定ファイルの例
これからの課題になるであろう点
■ アプリごとに管理方法が違う
● 新規アプリは増える
● 案件特化になると使いまわせない
● 学習コストが高くなる
12
PHPで管理 TOMLで管理 新しい管理方法?
New!!
バナーツールに求められること
■ 非エンジニアが運用できること
● GUIで設定できる
● 設定の手順が少ない
● バリデーションがしっかり行える
■ 案件が増えても使えること
● 共通機能を提供する
● 案件ごとにうまくカスタマイズできる余地がある
● 案件ごとに環境をいくつも用意しなくて良い
■ 工数がかからないこと
● 短期間・少人数で開発できる
● アプリのリリースサイクルをブロックしない
13
バナーツールの構想
■ コアとなるエンジンは共通
● 複数のアプリで使えるようにする
● 出来る限り汎用的に作る
■ UIは案件に特化したものにしたい
● 案件固有の事情をUIで吸収
● 運用者が使いやすいUIを提供
● 最悪UIは案件ごとに作っても構わない
14
汎用性 v.s. 特化型
15
VS
100徳ナイフ
パイナップルむき機
使いやすいツールとは?
■ 用途に特化したツール
● 使用者が迷わない
● 余計な入力項目がない
● 学習コストが低い
16
汎用的なツールとは?
■ 使い回しが効くツール
● 案件が増えても対応できる
● カスタマイズしやすい
● 対応できる幅が大きい
17
使いやすくて汎用的なツールを作る
■ 表面と基盤で汎用性を分ける
● UIは案件特化
● コアとなる部分は汎用的にする
18
汎用的な基盤
使いやすいUI 使いやすいUI 使いやすいUI
配信条件の汎用化
■ 配信条件を式で書けるようにする
● os == "iOS" のように書ける
● 変数が使える
● 複雑な条件でも表現ができる
19
汎用的な基盤
条件式の評価エンジン
配信条件式の設定の問題点
■ 条件式の設定の難易度が高い
● バナーの条件はそんなに単純ではない
● 非エンジニアに入力させるのは大変
● 入力ミスがあった場合どうするのか?
20
os == "iOS" && (tab == 1 || tab == 2) && is_beginer
例:iOSユーザのビギナー向けにタブ1とタブ2にバナーを出す
条件式の保存
■ 条件式の構成要素を保存する
● 設定される条件の種類は多くない
● 多くの場合共通の条件の組み合わせ
● 予め使う条件の構成要素を保存しておく
● 選択式にする
21
os == "iOS" && (tab == 1 || tab == 2) && is_beginer
OSの種類 表示タブ ビギナー
UIの自動生成
■保存されている式からUIを自動生成する
● 保存されている式をUIで選択する
● ANDやORで組み合わせる
22
汎用的な基盤
条件式の評価エンジン
使いやすいUI 使いやすいUI 使いやすいUI
自動生成 自動生成 自動生成
開発コストの問題
■ 汎用的にするのはいいがいつできるのか?
● 条件式の評価器
● UIの自動生成エンジン
● どちらも重たい機能
23
開発コストの軽減
■ 条件式の評価器
● Goの式としてパースする
● Goの標準パッケージを利用
● イチから作る必要はない
■ UIの自動生成
● JSON Schema + JSON Editorの利用
● JSON SchemaからWeb UIを自動生成
● 式からJSON Schemaを生成する
24
デモ2:配信条件の設定
25
https://youtu.be/Xjpw9DOEe9g
案件ごとに異なる設定項目の吸収
■ レスポンスをJSON Schemaで定義
● JSON Schemaで予め記述
● JSON EditorでUIの自動生成
26
バナーツール
・配信条件
・レスポンス
バナー取得
OS, APIバージョン などの変数
条件に当てはまるバナー
画像URL, 遷移先URL
アプリ
デモ3:レスポンススキーマの設定
27
https://youtu.be/L61IaI-zqyk
運用コストの問題と解決
■ 利用案件の数は増えていく
● 案件が増える度に環境を用意する?
● 機能を追加する度に反映する?
● 案件間でバージョンが乖離していく
■ マルチテナント型にする
● 1ソース+2環境で運用する
● QA環境+本番環境
● GAEのNamespace APIを使う
● 案件ごとにデータだけ別に保存
28
デモ4:Namespaceの追加
29
https://youtu.be/S9r4_0nyo8o
汎用性の維持と機能拡張
30
機能拡張の課題
31
■ 機能拡張は必要になる
● 案件ごとに違う機能要件
● 影響範囲が大きい
● どういう機能を足していけば良いのか
■ 汎用性が壊れないようにする
● 特化しすぎた機能は作らない
● JSON Schemaでだいたい対応できる
● 要望のもう1つ下の基盤から作る
● 機能を最小にする
安全に機能拡張を行うには?
■ 機能追加は多くの案件に影響する
● 影響範囲を小さくする
● 破壊的な変更はしない
● できるだけ新規追加のみにする
● コアの部分には手を入れない
● 管理画面の機能拡張に留める
32
機能追加の動機づけ
■ どんな機能をいつ追加するのか?
● 利用者から要望のある機能を追加する
● 利用者の問題を解決する機能を追加する
● 利用者がテストしてくれる
● 親切心で100徳アーミーナイフは作らない
33
機能拡張の例
■ 一括アップロード
● 数百件の項目を入力したい
● 一気に入力できるようにしたい
● CSVをアップロードできる
● 複数の案件で利用できる
34
一括アップロードの実現
■ どう実現するのか
● CSVは1行で1件を表す
● JSONはオブジェクトの入れ子もあり得る
● 案件ごとにレスポンスの形式は違う
● JSON Schemaを元にCSVをパースする
35
url image.background image.main
http://example.com http://example.com/b.png http://example.com/m.png
{
"url": "http://example.com",
"image": {
"background": "http://example.com/b.png",
"main": "http://example.com/m.png"
}
}
バナーツールを支える技術
36
条件式と静的解析
37
配信条件式の評価
■ バナーの配信条件に条件式を用いる
● Goの式としてパースできる独自形式
● 型定義を式で表現できるようにしてある
38
バナー取得
GET /banner/?os=1
String(os) == "1"
バナー画像URL
配信条件:
バナーツール
goパッケージ
■ 標準パッケージとして静的解析の機能を提供
go/ast 抽象構文木(AST)を提供
go/build パッケージに関する情報を集める
go/constant 定数に関する型を提供
go/doc ドキュメントをASTから取り出す
go/format コードフォーマッタの機能を提供
go/importer コンパイラに適したImporterを提供
go/parser 構文解析の機能を提供
go/printer ASTの表示機能を提供
go/scanner 字句解析の機能を提供
go/token トークンに関する型を提供
go/types 型チェックに関する機能を提供
39
条件式評価器の実装
40
■ goパッケージを用いて式の評価器を作る
● go/parserパッケージで式のASTを取得
● ASTを再帰的に評価していく
■ 定数評価器をうまく使う
● go/constantパッケージを使う
● constant.Valueに変換する
==
"A" "B"
パース 定数評価
参考:goパッケージで簡単に静的解析して世界を広げよう
"A" == "B" false
定数評価器の利用
■ go/constantパッケージ
● 定数同士の演算ができる
41
constant.Valueに変換できれば、
簡単に演算をやってくれる
func BinaryOp(x Value,op token.Token,y Value) Value
例:2項演算を行う関数
型付き変数の実装
■ 式の中で型を表す
● 関数呼び出しを変数として扱う
● 大文字で始まる関数呼び出しのみ
● 引数は識別子のみでそれを変数名とする
■ 変数を評価する
● 式の評価時には値が決まっている
● 評価器から見ると名前付きの定数に近い
● constant.Valueにして評価
42
型 変数名
Int(n) == 10
型をバリデーションに利用する
■ バリデーションを行う
● JSON Schemaを使えばJSONがスキーマを
満たしているかチェックできる
● 変数に型を導入することで、型情報を基にス
キーマを定義できる
● 型に沿ったバリデーションができる
■ オリジナル型を作る
● 組込み型としてオリジナルの型を作れる
○ URL, IntRange など
● バリデーションも行える
43
ゼロ値をバリデーションに利用する
■ 式を評価するとboolになるか判定する
● 条件式中に変数があると評価できない
● 変数にはゼロ値がある
● 変数にゼロ値を設定してみて評価する
○ 評価結果がboolになればOK
44
Int(n) == 10
例1:
0 == 10 false
Int(n) + 10
例2:
0 + 10 10
関数の実現
■ 関数を使えるようにする
● 小文字から始まる関数呼び出しを対象
● 引数は任意の式
● オリジナルの組込み関数のみ使用できる
○ cond, until, hours, days,...
■ 関数呼び出しを評価する
● const.Valueを引数と戻り値に取る関数
● reflect.MakeFuncの思想と似ている
● 関数呼び出しごとにスコープを作る
○ 別の式を呼び出すcond関数のため
45
関数の例
■ 引数と戻り値をconstant.Valueにする
46
※スペースの都合上、エラー処理は省いてある
func add(args []constant.Value) []constant.Value {
x, _ := strconv.ParseInt(args[0].String(),10,64)
y, _ := strconv.ParseInt(args[1].String(),10,64)
return []constant.Value{
constant.MakeInt64(x + y),
}
}
例:足し算
条件式を保存して呼び出す
■ cond関数
● よく使う式をDBに保存しておける
● cond関数を使って呼び出せる
● 呼び出し時に変数に値を束縛できる
47
String(os) == Enum(__os, "0,1", "iOS,Android")
保存された式 (id:1)
cond(1,"__os=1")
呼び出す式
列挙型の変数
変数の値
呼び出す式のID
String(os) == "1"
展開した式
ASTの操作とあいまい検索
■ 条件式を検索する
● 変数の一部を与えてtrueになる式を検索する
● condを展開する必要がある
● 与えられなかった変数を使っている部分を無視して評価
する
48
&&
== ==
v1 1 v22
&&
== true
v1 1
&&か||がくるまで
上にのぼる
&&ならtrue
||ならfalseに置き換える
v1が与えられた場合
UIの自動生成
49
JSON SchemaとUIの自動生成
■ JSON Editor
● JSON SchemaからWeb UIを作る
● 保存されたデータからJSON Schemaを自動
生成してやればよい
50
※画像はJSON Editorのリポジトリより
条件式からUIを自動生成する
■ 保存された式を組み合わせる
● __で始まる変数をUIで設定する
● UIで設定した変数を束縛する形で
cond関数を用いてバナーの条件式とする
51
String(os) == Enum(__os, "0,1", "iOS,Android")
os: iOS ▼
Android
保存された式 (id:1)
cond(1,"__os=1")JSON Schema
バナーに設定される式
作業者は保存された条件を
ANDやORで組み合わせるだけで済む
UIが生成される 選んだ値を設定する
GAEによるマルチテナントな
Webアプリの構築
52
マルチテナント型のWebアプリとは?
■ マルチテナント型
● 1つのシステムを複数のユーザ(企業)に提供する
53
Datastore
mercari atte kauru
Memcache
mercari atte kauru
アプリ バナーツールGAEプロジェクト
mercari
atte
kauru
GAEでNamespace APIを使ってマルチテナント型を実現
Namespace APIとは?
54
■ Namespace API
● Namespaceを分けることのできるAPI
● 利用可能なAPI
○ Datastore, Memcache, Task Queue, Search
■ Namespaceを分ける理由
● Namespace間でデータの干渉を防ぐ
● マルチテナント型のWebアプリを作れる
URLとルーティングルール
■ GAEのURLは以下のようにアクセスできる
● Inst: インスタンス(数値)
● Ver: バージョン
● Serv: サービス
● AppID: アプリケーションID
■ ゆるいルーティング
● インスタンス、バージョン、サービスが存在しないとデ
フォルトのものにルーティングされる
● インスタンスorバージョンの代わりにNamespaceを書く
55
<Inst>-dot-<Ver>-dot-<Serv>-dot-<AppID>.appspot.com
<NS>-dot-<Ver>-dot-<Serv>-dot-<AppID>.appspot.com
マルチテナント型にしときの課題
■ Namespace間の設定の移行
● 開発環境を用意する際にほしい
● 移行するAPIを用意する必要がある
■ バグが出ると全滅する
● すべて同じソースコードを使ってる
● バージョンをうまく使って移行する
■ ローカルでのデバッグが面倒
● ローカルのコンソールのNamespaceの対応が貧弱
● MemcacheはNamespaceを指定できない
56
まとめ
● モバイルアプリとコンテンツ管理
○ モバイルアプリのコンテンツ管理は複雑
○ 共通化が難しい
● バナーツール開発の背景と実践
○ 汎用性と使いやすいツールの実現
● 汎用性の維持と機能拡張
○ 案件ファーストで破壊的な変更をいれない
● バナーツールを支える技術
○ 静的解析
○ JSON EditorによるUI自動生成
○ GAEによるマルチテナント型Webアプリ
57
Thank you!
twitter: @tenntenn
Qiita: tenntenn
connpass: tenntenn
58

More Related Content

PDF
Namespace API を用いたマルチテナント型 Web アプリの実践
PDF
Cloud functionsの紹介
PDF
goパッケージで型情報を用いたソースコード検索を実現する
PDF
Gopher Fest 2017参加レポート
PDF
Cloud Functionsの紹介
PDF
粗探しをしてGoのコントリビューターになる方法
PDF
GoによるiOSアプリの開発
PDF
オススメの標準・準標準パッケージ20選
Namespace API を用いたマルチテナント型 Web アプリの実践
Cloud functionsの紹介
goパッケージで型情報を用いたソースコード検索を実現する
Gopher Fest 2017参加レポート
Cloud Functionsの紹介
粗探しをしてGoのコントリビューターになる方法
GoによるiOSアプリの開発
オススメの標準・準標準パッケージ20選

What's hot (20)

PDF
Goだけでモバイルアプリを作ろう
PDF
Go mobileでモバイルアプリを作ろう
PDF
Google Assistant関係のセッションまとめ
PDF
エディタの壁を越えるGoの開発ツールの文化と作成法
PDF
go.mobile で Android 開発
PDF
Goだけでモバイルアプリを作る
PDF
Go Friday 傑作選
PDF
GAE/GoでLINE Messaging API を使う
PDF
条件式評価器の実装による管理ツールの抽象化
PPTX
ネットワークの切り替えを感知する方法
PDF
Go初心者向けハンズオン コマンドラインツールを作ろう
PDF
過去に自作したGoプロダクトの紹介 - Goオールスターズ
PDF
Goでwebアプリを開発してみよう
PDF
Go MobileでAndroidアプリ開発
PDF
GAE/Goとsyncパッケージ
PDF
Goにおけるバージョン管理の必要性 − vgoについて −
PDF
Go1.8 for Google App Engine
PDF
僕がAndroid開発する時にちょっと便利だと思うtips
PDF
Goでかんたんソースコードの静的解析
PDF
GAE/GoでWebアプリ開発入門
Goだけでモバイルアプリを作ろう
Go mobileでモバイルアプリを作ろう
Google Assistant関係のセッションまとめ
エディタの壁を越えるGoの開発ツールの文化と作成法
go.mobile で Android 開発
Goだけでモバイルアプリを作る
Go Friday 傑作選
GAE/GoでLINE Messaging API を使う
条件式評価器の実装による管理ツールの抽象化
ネットワークの切り替えを感知する方法
Go初心者向けハンズオン コマンドラインツールを作ろう
過去に自作したGoプロダクトの紹介 - Goオールスターズ
Goでwebアプリを開発してみよう
Go MobileでAndroidアプリ開発
GAE/Goとsyncパッケージ
Goにおけるバージョン管理の必要性 − vgoについて −
Go1.8 for Google App Engine
僕がAndroid開発する時にちょっと便利だと思うtips
Goでかんたんソースコードの静的解析
GAE/GoでWebアプリ開発入門
Ad

Viewers also liked (13)

PDF
Static Analysis in Go
PDF
Goにおける静的解析と製品開発への応用
PDF
Javaトラブルに備えよう #jjug_ccc #ccc_h2
PPTX
うしちゃん WebRTC Chat on SkyWayの開発コードw
PDF
HTTP2 RFC 発行記念祝賀会
PDF
Mobile Apps by Pure Go with Reverse Binding
PDF
Go静的解析ハンズオン
PDF
メルカリ・ソウゾウでは どうGoを活用しているのか?
PDF
HTTP2 時代の Web - web over http2
PPTX
WebRTC Browsers n Stacks Implementation differences
PDF
Go入門
PDF
エキスパートGo
PDF
メルカリ カウルのマスタデータの更新
Static Analysis in Go
Goにおける静的解析と製品開発への応用
Javaトラブルに備えよう #jjug_ccc #ccc_h2
うしちゃん WebRTC Chat on SkyWayの開発コードw
HTTP2 RFC 発行記念祝賀会
Mobile Apps by Pure Go with Reverse Binding
Go静的解析ハンズオン
メルカリ・ソウゾウでは どうGoを活用しているのか?
HTTP2 時代の Web - web over http2
WebRTC Browsers n Stacks Implementation differences
Go入門
エキスパートGo
メルカリ カウルのマスタデータの更新
Ad

Similar to 静的解析とUIの自動生成を駆使してモバイルアプリの運用コストを大幅に下げた話 (20)

PPTX
HTML5 conference 2013
PDF
【2018/09/11】PAYでのReact Nativeにおける APIクライアント実装 について
PDF
メルカリアッテの実務で使えた、GAE/Goの開発を効率的にする方法
PDF
Hypermedia: The Missing Element to Building Adaptable Web APIs in Rails (増補日本語版)
PDF
関西FirefoxOS勉強会6thGiG「アプリ間通信」
PDF
スタートアップ向け!1人日でできるサービスの高速化方法と成果
PDF
GoによるWebアプリ開発のキホン
PDF
Go言語入門者が Webアプリケーション を作ってみた話 #devfest #gdgkyoto
PDF
実践Go ツールの作成から配布まで
PDF
Go言語で作る webアプリ@gocon 2013 spring
PDF
ヒカルのGo 資料 Webアプリケーションの作り方
PDF
【とらラボLT】go言語でのweb apiの作り方3選
PDF
Dist 29 gcp_serverless_web_app_development
PDF
ヤフオク!の快適なカスタマー体験を支えるモバイルアプリのライブアップデート技術
PDF
Sinatraでwebアプリケーション開発を学ぶ
PDF
KituraとサーバーサイドSwift
PDF
らくちん Go言語
PDF
Google APP Engine vs リアルタイムウェブ
PDF
Ginとbindataで作るシングルバイナリWebApp
PPTX
Gunosy Beer Bash #05 pairs
HTML5 conference 2013
【2018/09/11】PAYでのReact Nativeにおける APIクライアント実装 について
メルカリアッテの実務で使えた、GAE/Goの開発を効率的にする方法
Hypermedia: The Missing Element to Building Adaptable Web APIs in Rails (増補日本語版)
関西FirefoxOS勉強会6thGiG「アプリ間通信」
スタートアップ向け!1人日でできるサービスの高速化方法と成果
GoによるWebアプリ開発のキホン
Go言語入門者が Webアプリケーション を作ってみた話 #devfest #gdgkyoto
実践Go ツールの作成から配布まで
Go言語で作る webアプリ@gocon 2013 spring
ヒカルのGo 資料 Webアプリケーションの作り方
【とらラボLT】go言語でのweb apiの作り方3選
Dist 29 gcp_serverless_web_app_development
ヤフオク!の快適なカスタマー体験を支えるモバイルアプリのライブアップデート技術
Sinatraでwebアプリケーション開発を学ぶ
KituraとサーバーサイドSwift
らくちん Go言語
Google APP Engine vs リアルタイムウェブ
Ginとbindataで作るシングルバイナリWebApp
Gunosy Beer Bash #05 pairs

静的解析とUIの自動生成を駆使してモバイルアプリの運用コストを大幅に下げた話