Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
広告システム刷新の舞台裏
PHPからJavaに変えてみました
ヤフー株式会社
森下大介
自己紹介
YJ入社前
Sierな世界の住人でした。
受託案件をこなす業務システム開発
のエンジニア。
YJ入社後
BtoBなWebサービス開発へ。
Web系と言いつつエンタープライズ
系っぽいシステムの開発に携わる。
• 名前:森下大介
• 年...
3P今回のお話の前提その1
自分の所属部署における取り組み事例です。
• 対象は、広告主向けの入稿やレポーティング機能を
提供する「業務系」と呼ばれるシステム。
• データ登録と集計系の比重が高いところがエンター
プライズシステムっぽい。YJの...
4P今回のお話の前提その2
刷新前のシステムをディスるつもりは無いです。
・・・ホントですよ?
ビジネスをしっかり支えてくれて、後から個別に
刷新もできる優れた構成でした。
5P本日の内容
• バックグラウンド
• 取り組んだこと
• やったことその0:体制づくり
• やったことその1:プログラミング言語を変える
• やったことその2:テストができるアーキテクチャに
• やったことその3:テスト向けDSL (Spo...
バックグラウンド
7P入社当時のシステムの特徴:利用言語、道具
• 言語:
– PHPメイン
– たまにバッチ系にPerl
– 一部のライブラリはC + PHP Extension
• 道具:
– ほとんどの人がvim, emacs等を使用
– IDEはほとんど...
8P入社当時のシステムの特徴:システム構成
• システム構成
– FE/BEは明確に分離、BEはWebAPI化
– マイクロサービスほどでは無いが分散システム化
• このおかげで・・・
– 機能ごとにスケールができた
– 機能ごとにアップデート...
9Pこんな感じの構成
A
Service
B
Service
C
Service
X
Service
Y
Service
Z
Service
バックエンドサーバー群1 バックエンドサーバー群2
FEサーバー群
バックエンド機能を
WebAPIで...
10P当時、思っていたこと
何をやっても
「なんか妙につらい・・・」
11P例1:小改修なのにつらい
typo修正したいだけなのに・・・
- ユニットテストがない
- コンパイラ、型がない
- IDE使ってない
12P例2:結合がつらい
なかなかつながらない・・・
特にAPIIFドキュメントが
- 手書きだし、書き方が統一されてない
- 書き手と読み手でお互いに認識がズレてる
- ドキュメント通りに実装されない
13P例3:なんとなく動くのがつらい
LLの柔軟さが裏目に・・・
- 存在しないメソッドコールしても動く。
- 間違った型で引数受け取っても動く。
14P何が起きていたのか?
考察1:「大規模」で「複雑」に。
サービスが育った結果・・・
- 道具の助け無しで
- 人手と記憶と努力と根性で
- がんばれる規模をこえている
15P何が起きていたのか?
考察2:道具とやり方が合ってない
ちゃんとデータを扱いたいのに
- 型宣言が無い
- コンパイラに頼れない
- 単体テストを仕掛けられない
16P何が起きていたのか?
考察3:積み重なる技術負債
やり方を変えないままで
- 既存コンポーネントは拡張され
- 新規コンポーネントが追加され
- そしてそこにテストは無い
17P何が起きていたのか?
考察4:チーム同士が良くも悪くも独立
分散システム化によって・・・
– 担当機能毎にチームの独立性が強い
– 技術/知識/事例の共有がされない
– 助け合う、融通しあう意識が弱い
18P当時のエンジニアの心中
このままだとヤバイのは薄々感じてる。
感じてるけど・・・
– 目の前の巨大システムは売上を現実にあげている
– ビジネス拡張のための案件はひっきりなし
– 変えるには根本的な手当が必要・・・
– どーすりゃいいのこ...
19Pそんなときに
組織変更がありました。新部長が言いました。
「イチから全部変えてよし」
このおかげで動き出すことが出来ました。
これくらいの上位レイヤから言い出してくれると格段
に動きやすくなると実感してます。
20P目指したこと
システム刷新は「結果」
• 自分の変化がアウトプットを変える。
• でも結果を出すことも重要。
• 結果で証明する。
変わるべきは「自分達」
• 3倍早く
• やりやすく
• 確実に
開発できる強いエンジニアになること。
「...
取り組んだこと
やったことその0
体制をつくる
23P体制づくり
リード役の配置
– 自分が当時の役職を離れて、部長直下の「部
付」ぼっちとなり、刷新活動に集中。
– できるかぎりディスカッションしてコンセン
サス取ることを努力するが迷ったら最後は決
断の責任を全て負う。
24P体制づくり
仲間を集める
– 同じような問題意識を持つ人に声をかけて、
バーチャルなチームを構築。
25P体制づくり
部門長による宣言
– 刷新していくことを部の内外でステークホル
ダー/部内メンバーに宣言。
やったことその1
プログラミング言語を変える
27Pプログラミング言語を変える
これを決めた時点で、
学習コストが極大化する事が確定。
大抵はそうしないで済む方向を考えるもので
すが・・・
28Pプログラミング言語を変える
「大規模で、
データをきっちり扱うシステム」
をちゃんと開発していくために必要な
変化だと判断。
29Pプログラミング言語を変える
とにかく欲しかったのは
– コンパイル
– 型宣言
30Pプログラミング言語を変える
特に求めた効果は、しょうもないレベルの間違いを
動かすまでもなく潰せること。
これができないと、typo修正すらおそろしくなる。
– 動かして全確認が必要
– 漏れが無いか確証が持てない。
31Pプログラミング言語を変える
この条件でいくと、
現実的な選択肢はだいぶ絞られる。
考えたのは以下あたり。
– C++
– Java
– Scala
32Pプログラミング言語を変える
選んだのは
Java
33Pプログラミング言語を変える
これで得られたもの
• コンパイルできる
• 厳密な型定義ができる
• 優れたメモリ管理(GC)
• 実行時最適化(JIT)
• デバッガ、解析ツール
副次的なものとして
• 豊富なOSS
• 統合開発環境の活...
やったことその2
テストできるアーキテクチャに
35Pテストできるアーキテクチャに
「テストできない」とはどういう状況?
• 動かす準備が大変
• CI/CDの中で実行できない
• ブラックボックステストになる
• etc…
36Pテストできるアーキテクチャに
根本的な原因として、アプリケーション内部が「モノ
リシック」なカタマリになっているためと仮定。
システム全体で見ると分散システム構成となっていた
が、個々のアプリケーション内部ではユニットテスト
したいクラス...
37Pモノリシックなコードの例
これらは一見するとA, B, Cというクラスに分
割されているように見えるが・・・
public class A {
public void x() {
B b = new B();
b.y();
}
}
pub...
38Pこれにテストケースを仕掛けてみると・・・
public class A {
public void x() {
B b = new B();
b.y();
}
}
public class B {
public void y() {
C ...
39Pテストできるアーキテクチャに
ポイントは、
「呼び出し先の実装クラスを自分でnewしていること」
これだとクラス同士が密結合する。
この状態で無理にテストケースを仕掛けたとしても、
• 実行条件が増える
• バリエーションが掛け算で増加
...
40Pテストできるアーキテクチャに
このようなことから、
「テストできるような構造じゃないからやらない」
という結論となり、放置される。
これを解決するために、アプリケーション内部の基本アー
キテクチャとして「Dependency Inject...
41PDI(JavaでSpringFramworkの場合)
public class Aimpl
implements A {
@Autowired
private B b;
public void x() {
b.y();
}
}
publi...
42Pこれにテストケースを仕掛けてみると・・・
public class Aimpl
implements A {
@Autowired
private B b;
public void x() {
b.y();
}
}
今まではA単独のテスト...
43Pテストできるアーキテクチャに
これにより、依存する他モジュールをnewしなくなる。
そうなると、テスト時にモックを自由に差し込めるようになるので
単独テストが可能となる。
ただし、絶対にあらゆるクラスのnewが禁止というわけではなく、
「...
44Pテストできるアーキテクチャに
ということで、テストが出来るようになる事を
目的としてDIコンテナを使用。
利用してるDIコンテナは
「SpringFramework」
やったことその3
テスト向けDSLの採用
46Pテスト向けDSLの採用
当初使おうとしていたのは定番のJUnit, JMock。
でもJUnitはJava言語でテストを書くことになるが、
Java言語はテストを表現する文法を持たない。
またJMockはかなり変態コードになるためキツイ。
47Pテスト向けDSLの採用
final SampleDao dao = context.mock(SampleDao.class);
final List<Sample> expected = Arrays.asList(new Sample...
48Pテスト向けDSLの採用
テストケースの作成に労力が掛かり過ぎるようだと、
「テスト書くのがキツすぎるのでやらない」
ということになる。
49Pテスト向けDSLの採用
選んだのは
Spock
50Pテスト向けDSLの採用
Junitの上に構築されたものだが、記述言語は、JVM言語の「Groovy」
その上にテストを記述するためのDSL(ドメイン固有言語)が構築されている。
主な機能としては
• テスト実行
• BDD的なテスト記述文...
51PSpockによるテストコード例
class SampleSTest extends Specification {
def “データ更新テスト(#testname)”() {
given:
def service= new SampleS...
やったことその4
CI/CD、静的解析
53PCI/CD, 静的解析
アーキテクチャとテストケースそれぞれの
アプローチからテストができない理由を取
り除いた。
これでやっとCI/CDの中であたりまえにテ
ストを行うようになった。
54PCI/CD, 静的解析
CI/CDの中では以下の様なテストや解析を実施
• SpockによるSテストケース実行
• Cloverによる詳細なカバレッジ計測
• Coverity QualityAdvisorによるコード解析
• Cover...
やったことその5
インタフェース定義言語
56Pインタフェース定義言語
分散システムの形でシステム全体を構成している
ので、バックエンドの各機能は
「Webサービス(API)」
としてFEや他BEに提供する形にしている。
57Pインタフェース定義言語
その実装とテストのためには以下を行うことになる。
• 外部仕様(APIIF)の定義と公開
• 外部仕様どおりの実装
• 結合試験
58Pインタフェース定義言語
APIを「提供」する側では、
• 人がAPIIFを考えて
• それを頑張ってドキュメントとして書く。
• それを元に実装する。
APIを「利用」する側では、
• 人がAPIIFドキュメントを読んで理解して、
• そ...
59Pインタフェース定義言語
「仕様/ドキュメント/実装」を人の脳が頑張って変換し
ながら何種類も成果物つくってるが・・・。
この変換時に認識違いによるズレがあった場合、それは結
合試験まで発見できない。
60Pインタフェース定義言語
でも・・・
IFって静的なものなんだから、宣言的に記述できるはず。
宣言的な記述ならそこからコードも文書も生成できる。
61Pインタフェース定義言語
導入したのが
インタフェース定義言語
62Pインタフェース定義言語
当初はOSSの以下あたりを使えないかと検証したが、
• Googole ProtocolBuffer
• Apache Thrift
以下の理由から断念
• 入力値バリデーションが表現できない
• ドキュメントが生...
63Pインタフェース定義言語
一から以下を行いました。
• 定義言語自体の文法設計
• コンパイラ開発
• Javaコード・ドキュメントジェネレータ開発
• ドキュメント表示サーバー開発
• 入力値バリデーションエンジン開発
64Pインタフェース定義言語
IDLを使ってAPIIF設計をすると・・・
65Pインタフェース定義言語
1.APIIFをIDL文法で記述
記述した内容はIDLコンパイラを通すことで整合
性チェックを行うことが出来る。
66Pインタフェース定義言語
2.ドキュメント生成して公開
ドキュメントジェネレータを通してJSONデータ
を生成し、それをドキュメント表示サーバーに
アップロードして公開する。
67Pインタフェース定義言語
3.Javaコード生成して利用
Javaコードジェネレータを通してコード生成し
て、実装で利用する。生成コードは編集は一切
せず利用のみとして、常に上書き更新可能にし
ている。
68Pインタフェース定義言語
IDLから生成されるJavaコードは以下。
• Context (データ操作のコンテキスト情報)
• Entity (Pojoデータオブジェクト)
• Enum (Enum系項目の値定義)
• Error (エラー...
69Pインタフェース定義言語
//IDLファイルの例
namespace entity sample.entity;
entity Sample {
field Long id {
valid min 1;
valid max 100;
}
f...
70Pインタフェース定義言語
//IDLファイルの例
namespace service sample.jaxrs;
service SampleService {
path /SampleService;
operation Response...
71Pインタフェース定義言語
72Pインタフェース定義言語
73Pインタフェース定義言語
システム刷新を
振り返っての所感
問題検出はできるだけ前工程に。
76P問題検出はできるだけ前工程に
システム開発は先の工程(結合試験とか総合試験
とか)に進めば進むほどソースコードが開発者の
手元を離れる。
そこで見つかった問題を修正して環境に届けるに
は相応の時間がかかる。
77P問題検出はできるだけ前工程に
開発者の手元を離れる直前まで、やれる事をやる。
• IFを結合前に安定させられるIDL
• コンパイルと型宣言
• テストを書いて手元でもCI/CDでも実行
• Coverityの静的解析。
テストを阻む要因を潰す
79Pテストを阻む要因を潰す
テストに向かないアーキテクチャや道具を使う
と対応コストを理由にやらないことが正当化さ
れやすい。
80Pテストを阻む要因を潰す
テストできない理由が消えると、エンジニアは
わりとちゃんとテスト書くようになる。
書いたほうがいいのは皆わかってるし、
書いたものは皆動かしたい。
81Pテストを阻む要因を潰す
アプリケーションアーキテクチャまで踏み込
んでテストを考慮することができればベスト。
ただこれが出来るのはかなり幸運なこと。
未熟でも早めに適用、
フィードバックを受けて磨く
83P早めに適用・フィードバックを受ける
やり方を大幅に変えた時は最初からいろいろ頑
張りたくなる。
でもそこは一旦最低限に押さえて早く実戦投入
することが大事。
84P早めに適用・フィードバックを受ける
机上で考えるよりも
実践の場で揉まれるほうが一番早い。
85P早めに適用・フィードバックを受ける
ただし最初の適用プロダクトでは途中で色々と
方針変更が入りがち。
それを承知してもらうのと、できればしがらみ
の無い新規プロダクトがベスト。
部門長サポートと仲間が大事
87P部門長サポートと仲間が大事
現場で「こうしたい」という思いがあっても、
仕事で開発をしている以上はビジネス要件への
対応は一番力を割くべきところ。
88P部門長サポートと仲間が大事
部門長や組織が
• 攻めの開発(ビジネス要件対応)
• 守りの開発(保守/刷新)
の両方を理解してくれて初めて効果的に取り組める
89P部門長サポートと仲間が大事
個人の能力とアウトプットはかなり限られる。
仲間がいれば、個の範囲を越えた成果が必ず出る。
Upcoming SlideShare
Loading in …5
×

#jjug_ccc #ccc_f1 広告システム刷新の舞台裏 - PHPからJavaに変えてみました

1,592 views

Published on

JJUG CCC 2016 Springの以下のセッションで発表した際の資料です。
http://www.java-users.jp/?page_id=2396#F-1

Published in: Engineering
  • Be the first to comment

#jjug_ccc #ccc_f1 広告システム刷新の舞台裏 - PHPからJavaに変えてみました

  1. 1. 広告システム刷新の舞台裏 PHPからJavaに変えてみました ヤフー株式会社 森下大介
  2. 2. 自己紹介 YJ入社前 Sierな世界の住人でした。 受託案件をこなす業務システム開発 のエンジニア。 YJ入社後 BtoBなWebサービス開発へ。 Web系と言いつつエンタープライズ 系っぽいシステムの開発に携わる。 • 名前:森下大介 • 年齢:41 • 職歴:2011年にYJに中途入社 • 言語:C/C++/Java/PHP/Node.js
  3. 3. 3P今回のお話の前提その1 自分の所属部署における取り組み事例です。 • 対象は、広告主向けの入稿やレポーティング機能を 提供する「業務系」と呼ばれるシステム。 • データ登録と集計系の比重が高いところがエンター プライズシステムっぽい。YJの中ではマイノリティ。
  4. 4. 4P今回のお話の前提その2 刷新前のシステムをディスるつもりは無いです。 ・・・ホントですよ? ビジネスをしっかり支えてくれて、後から個別に 刷新もできる優れた構成でした。
  5. 5. 5P本日の内容 • バックグラウンド • 取り組んだこと • やったことその0:体制づくり • やったことその1:プログラミング言語を変える • やったことその2:テストができるアーキテクチャに • やったことその3:テスト向けDSL (Spock) • やったことその4:CI/CD, 静的解析 • やったことその5:インタフェース定義言語 • 振り返っての所感
  6. 6. バックグラウンド
  7. 7. 7P入社当時のシステムの特徴:利用言語、道具 • 言語: – PHPメイン – たまにバッチ系にPerl – 一部のライブラリはC + PHP Extension • 道具: – ほとんどの人がvim, emacs等を使用 – IDEはほとんど使われていない – テストはほぼブラックボックステストのみ
  8. 8. 8P入社当時のシステムの特徴:システム構成 • システム構成 – FE/BEは明確に分離、BEはWebAPI化 – マイクロサービスほどでは無いが分散システム化 • このおかげで・・・ – 機能ごとにスケールができた – 機能ごとにアップデートができた
  9. 9. 9Pこんな感じの構成 A Service B Service C Service X Service Y Service Z Service バックエンドサーバー群1 バックエンドサーバー群2 FEサーバー群 バックエンド機能を WebAPIで構成。 複数Serviceの集まりを 1アプリケーションにして サーバーにデプロイ。
  10. 10. 10P当時、思っていたこと 何をやっても 「なんか妙につらい・・・」
  11. 11. 11P例1:小改修なのにつらい typo修正したいだけなのに・・・ - ユニットテストがない - コンパイラ、型がない - IDE使ってない
  12. 12. 12P例2:結合がつらい なかなかつながらない・・・ 特にAPIIFドキュメントが - 手書きだし、書き方が統一されてない - 書き手と読み手でお互いに認識がズレてる - ドキュメント通りに実装されない
  13. 13. 13P例3:なんとなく動くのがつらい LLの柔軟さが裏目に・・・ - 存在しないメソッドコールしても動く。 - 間違った型で引数受け取っても動く。
  14. 14. 14P何が起きていたのか? 考察1:「大規模」で「複雑」に。 サービスが育った結果・・・ - 道具の助け無しで - 人手と記憶と努力と根性で - がんばれる規模をこえている
  15. 15. 15P何が起きていたのか? 考察2:道具とやり方が合ってない ちゃんとデータを扱いたいのに - 型宣言が無い - コンパイラに頼れない - 単体テストを仕掛けられない
  16. 16. 16P何が起きていたのか? 考察3:積み重なる技術負債 やり方を変えないままで - 既存コンポーネントは拡張され - 新規コンポーネントが追加され - そしてそこにテストは無い
  17. 17. 17P何が起きていたのか? 考察4:チーム同士が良くも悪くも独立 分散システム化によって・・・ – 担当機能毎にチームの独立性が強い – 技術/知識/事例の共有がされない – 助け合う、融通しあう意識が弱い
  18. 18. 18P当時のエンジニアの心中 このままだとヤバイのは薄々感じてる。 感じてるけど・・・ – 目の前の巨大システムは売上を現実にあげている – ビジネス拡張のための案件はひっきりなし – 変えるには根本的な手当が必要・・・ – どーすりゃいいのこれ?
  19. 19. 19Pそんなときに 組織変更がありました。新部長が言いました。 「イチから全部変えてよし」 このおかげで動き出すことが出来ました。 これくらいの上位レイヤから言い出してくれると格段 に動きやすくなると実感してます。
  20. 20. 20P目指したこと システム刷新は「結果」 • 自分の変化がアウトプットを変える。 • でも結果を出すことも重要。 • 結果で証明する。 変わるべきは「自分達」 • 3倍早く • やりやすく • 確実に 開発できる強いエンジニアになること。 「3倍早い開発スピード」
  21. 21. 取り組んだこと
  22. 22. やったことその0 体制をつくる
  23. 23. 23P体制づくり リード役の配置 – 自分が当時の役職を離れて、部長直下の「部 付」ぼっちとなり、刷新活動に集中。 – できるかぎりディスカッションしてコンセン サス取ることを努力するが迷ったら最後は決 断の責任を全て負う。
  24. 24. 24P体制づくり 仲間を集める – 同じような問題意識を持つ人に声をかけて、 バーチャルなチームを構築。
  25. 25. 25P体制づくり 部門長による宣言 – 刷新していくことを部の内外でステークホル ダー/部内メンバーに宣言。
  26. 26. やったことその1 プログラミング言語を変える
  27. 27. 27Pプログラミング言語を変える これを決めた時点で、 学習コストが極大化する事が確定。 大抵はそうしないで済む方向を考えるもので すが・・・
  28. 28. 28Pプログラミング言語を変える 「大規模で、 データをきっちり扱うシステム」 をちゃんと開発していくために必要な 変化だと判断。
  29. 29. 29Pプログラミング言語を変える とにかく欲しかったのは – コンパイル – 型宣言
  30. 30. 30Pプログラミング言語を変える 特に求めた効果は、しょうもないレベルの間違いを 動かすまでもなく潰せること。 これができないと、typo修正すらおそろしくなる。 – 動かして全確認が必要 – 漏れが無いか確証が持てない。
  31. 31. 31Pプログラミング言語を変える この条件でいくと、 現実的な選択肢はだいぶ絞られる。 考えたのは以下あたり。 – C++ – Java – Scala
  32. 32. 32Pプログラミング言語を変える 選んだのは Java
  33. 33. 33Pプログラミング言語を変える これで得られたもの • コンパイルできる • 厳密な型定義ができる • 優れたメモリ管理(GC) • 実行時最適化(JIT) • デバッガ、解析ツール 副次的なものとして • 豊富なOSS • 統合開発環境の活用 • 優れた静的解析ツール
  34. 34. やったことその2 テストできるアーキテクチャに
  35. 35. 35Pテストできるアーキテクチャに 「テストできない」とはどういう状況? • 動かす準備が大変 • CI/CDの中で実行できない • ブラックボックステストになる • etc…
  36. 36. 36Pテストできるアーキテクチャに 根本的な原因として、アプリケーション内部が「モノ リシック」なカタマリになっているためと仮定。 システム全体で見ると分散システム構成となっていた が、個々のアプリケーション内部ではユニットテスト したいクラス単位で単独で動かせない状態。
  37. 37. 37Pモノリシックなコードの例 これらは一見するとA, B, Cというクラスに分 割されているように見えるが・・・ public class A { public void x() { B b = new B(); b.y(); } } public class B { public void y() { C c = new C(); c.z(); } } public class C { public void z() { DB操作とか } }
  38. 38. 38Pこれにテストケースを仕掛けてみると・・・ public class A { public void x() { B b = new B(); b.y(); } } public class B { public void y() { C c = new C(); c.z(); } } public class C { public void z() { DB操作とか } } TestCaseA (A, B, Cの複合テスト) Class AにテストケースつくるとB, Cも必ず くっついて来て単独テストにならない。 TestCaseB (BとCの複合テスト) TestCaseC (これはまだ単独)
  39. 39. 39Pテストできるアーキテクチャに ポイントは、 「呼び出し先の実装クラスを自分でnewしていること」 これだとクラス同士が密結合する。 この状態で無理にテストケースを仕掛けたとしても、 • 実行条件が増える • バリエーションが掛け算で増加 • メンテ、問題箇所の特定が困難
  40. 40. 40Pテストできるアーキテクチャに このようなことから、 「テストできるような構造じゃないからやらない」 という結論となり、放置される。 これを解決するために、アプリケーション内部の基本アー キテクチャとして「Dependency Injection」を導入。
  41. 41. 41PDI(JavaでSpringFramworkの場合) public class Aimpl implements A { @Autowired private B b; public void x() { b.y(); } } public class Bimpl implments B { @Autowired private C c; public void y() { c.z(); } } public class Cimpl implements C { public void z() { DB操作とか } } 実装クラスとインタフェースを分離、コール先のインタフェースのみ認識 し、実装インスタンス(Dependency)は外部から注入(Injection)。
  42. 42. 42Pこれにテストケースを仕掛けてみると・・・ public class Aimpl implements A { @Autowired private B b; public void x() { b.y(); } } 今まではA単独のテストができなかったが、依存するBをモック化するこ とでテストしたい処理だけに対して確認を行えるようになる。 class TestCaseA { def testA() { def a = new Aimpl() //モック注入 a.b = Mock(B.class) //テスト実行、assert a.x() } }
  43. 43. 43Pテストできるアーキテクチャに これにより、依存する他モジュールをnewしなくなる。 そうなると、テスト時にモックを自由に差し込めるようになるので 単独テストが可能となる。 ただし、絶対にあらゆるクラスのnewが禁止というわけではなく、 「テストの都合で分離しててほしい単位」 でこれが適用されていればよい。
  44. 44. 44Pテストできるアーキテクチャに ということで、テストが出来るようになる事を 目的としてDIコンテナを使用。 利用してるDIコンテナは 「SpringFramework」
  45. 45. やったことその3 テスト向けDSLの採用
  46. 46. 46Pテスト向けDSLの採用 当初使おうとしていたのは定番のJUnit, JMock。 でもJUnitはJava言語でテストを書くことになるが、 Java言語はテストを表現する文法を持たない。 またJMockはかなり変態コードになるためキツイ。
  47. 47. 47Pテスト向けDSLの採用 final SampleDao dao = context.mock(SampleDao.class); final List<Sample> expected = Arrays.asList(new Sample()); Expectations mockConfig = new Expectations() { { oneOf(dao).findBySelector(with(new TypeSafeMatcherSimple<SampleSelector>() { @Override protected boolean matchesSafely(SampleSelector item) { assertEquals(ids[0], item.getIds().get(0)); assertEquals(ids[1], item.getIds().get(1)); assertEquals(userStatuses[0], item.getUserStatuses().get(0)); return true; } })); will(returnValue(expected)); } }; context.checking(mockConfig); JMockを利用した テストコードの一部。 つらくないですか?
  48. 48. 48Pテスト向けDSLの採用 テストケースの作成に労力が掛かり過ぎるようだと、 「テスト書くのがキツすぎるのでやらない」 ということになる。
  49. 49. 49Pテスト向けDSLの採用 選んだのは Spock
  50. 50. 50Pテスト向けDSLの採用 Junitの上に構築されたものだが、記述言語は、JVM言語の「Groovy」 その上にテストを記述するためのDSL(ドメイン固有言語)が構築されている。 主な機能としては • テスト実行 • BDD的なテスト記述文法 • 柔軟なモック/スタブ生成 • テストパターンデータの記述 特に良いのが、Groovy自体がJava よりも色々省略して書けること。 テストコードはJava言語では書き たくない。
  51. 51. 51PSpockによるテストコード例 class SampleSTest extends Specification { def “データ更新テスト(#testname)”() { given: def service= new SampleServiceImpl() def dao = Mock(SampleDao.class) 1 * dao.update(_) >> response serivce.dao = dao when: def result = service.update(request) then: assert result == response where: testname | request | response “パターンA” | “foo1” | “bar1” “パターンB” | “foo2” | “bar2” } } テストケースで以下のようなブロックに区 切ってコードを書ける。 • givenがテスト対象のセットアップ • whenがテスト対象の実行 • thenがテスト結果の確認 • whereがテストデータ Mockの生成とその挙動も全てテストコード の中で記述できるのが良いところ。 テストデータを複数件かけばその件数分で 全体をループして実行してくれる。 Groovyの型推論や省略記法も楽で助かる。
  52. 52. やったことその4 CI/CD、静的解析
  53. 53. 53PCI/CD, 静的解析 アーキテクチャとテストケースそれぞれの アプローチからテストができない理由を取 り除いた。 これでやっとCI/CDの中であたりまえにテ ストを行うようになった。
  54. 54. 54PCI/CD, 静的解析 CI/CDの中では以下の様なテストや解析を実施 • SpockによるSテストケース実行 • Cloverによる詳細なカバレッジ計測 • Coverity QualityAdvisorによるコード解析 • Coverity TestAdvisorによるテスト解析 • Frisbyを使ったWebAPIのSmokeテスト
  55. 55. やったことその5 インタフェース定義言語
  56. 56. 56Pインタフェース定義言語 分散システムの形でシステム全体を構成している ので、バックエンドの各機能は 「Webサービス(API)」 としてFEや他BEに提供する形にしている。
  57. 57. 57Pインタフェース定義言語 その実装とテストのためには以下を行うことになる。 • 外部仕様(APIIF)の定義と公開 • 外部仕様どおりの実装 • 結合試験
  58. 58. 58Pインタフェース定義言語 APIを「提供」する側では、 • 人がAPIIFを考えて • それを頑張ってドキュメントとして書く。 • それを元に実装する。 APIを「利用」する側では、 • 人がAPIIFドキュメントを読んで理解して、 • そのAPIを利用する処理を実装する。
  59. 59. 59Pインタフェース定義言語 「仕様/ドキュメント/実装」を人の脳が頑張って変換し ながら何種類も成果物つくってるが・・・。 この変換時に認識違いによるズレがあった場合、それは結 合試験まで発見できない。
  60. 60. 60Pインタフェース定義言語 でも・・・ IFって静的なものなんだから、宣言的に記述できるはず。 宣言的な記述ならそこからコードも文書も生成できる。
  61. 61. 61Pインタフェース定義言語 導入したのが インタフェース定義言語
  62. 62. 62Pインタフェース定義言語 当初はOSSの以下あたりを使えないかと検証したが、 • Googole ProtocolBuffer • Apache Thrift 以下の理由から断念 • 入力値バリデーションが表現できない • ドキュメントが生成できない
  63. 63. 63Pインタフェース定義言語 一から以下を行いました。 • 定義言語自体の文法設計 • コンパイラ開発 • Javaコード・ドキュメントジェネレータ開発 • ドキュメント表示サーバー開発 • 入力値バリデーションエンジン開発
  64. 64. 64Pインタフェース定義言語 IDLを使ってAPIIF設計をすると・・・
  65. 65. 65Pインタフェース定義言語 1.APIIFをIDL文法で記述 記述した内容はIDLコンパイラを通すことで整合 性チェックを行うことが出来る。
  66. 66. 66Pインタフェース定義言語 2.ドキュメント生成して公開 ドキュメントジェネレータを通してJSONデータ を生成し、それをドキュメント表示サーバーに アップロードして公開する。
  67. 67. 67Pインタフェース定義言語 3.Javaコード生成して利用 Javaコードジェネレータを通してコード生成し て、実装で利用する。生成コードは編集は一切 せず利用のみとして、常に上書き更新可能にし ている。
  68. 68. 68Pインタフェース定義言語 IDLから生成されるJavaコードは以下。 • Context (データ操作のコンテキスト情報) • Entity (Pojoデータオブジェクト) • Enum (Enum系項目の値定義) • Error (エラーコード一覧) • JAX-RSインタフェース(APIエンドポイント)
  69. 69. 69Pインタフェース定義言語 //IDLファイルの例 namespace entity sample.entity; entity Sample { field Long id { valid min 1; valid max 100; } field String name { valid max 100; valid pattern ”^[a-zA-Z0-9¥¥-_]+$”; } } //生成Javaコードの例 package sample.entity; public class Sample implements Entity { @CheckNumber(min=1, max=100) private Long id; public void setId(Long id) { …. } public Long getId() { …. } @CheckString(max=100, pattern=“^[a-zA-Z0-9¥¥-_]+$”) public String name; public void setName(String name) { …. } public String getName() { …. } } IDLコンパイラで Javaコード生成 IDLで記述したデータ構造(entity)のJavaコード生成例。 この場合はバリデーション用アノテーション付きのPojoが生成される。
  70. 70. 70Pインタフェース定義言語 //IDLファイルの例 namespace service sample.jaxrs; service SampleService { path /SampleService; operation Response add(Sample sample); operation Response set(Sample sample); } //生成Javaコードの例 package sample.jaxrs; @Path(“/SampleService) public interface SampleService { @Path(“/add”) @POST public Response add(Sample sample); @Path(“/set”) @POST public Response set(Sample sample); } IDLコンパイラで Javaコード生成 IDLで記述したWebAPIエンドポイントのJavaコード生成例。 この場合はJAX-RSのアノテーション付きインタフェースが生成される。
  71. 71. 71Pインタフェース定義言語
  72. 72. 72Pインタフェース定義言語
  73. 73. 73Pインタフェース定義言語
  74. 74. システム刷新を 振り返っての所感
  75. 75. 問題検出はできるだけ前工程に。
  76. 76. 76P問題検出はできるだけ前工程に システム開発は先の工程(結合試験とか総合試験 とか)に進めば進むほどソースコードが開発者の 手元を離れる。 そこで見つかった問題を修正して環境に届けるに は相応の時間がかかる。
  77. 77. 77P問題検出はできるだけ前工程に 開発者の手元を離れる直前まで、やれる事をやる。 • IFを結合前に安定させられるIDL • コンパイルと型宣言 • テストを書いて手元でもCI/CDでも実行 • Coverityの静的解析。
  78. 78. テストを阻む要因を潰す
  79. 79. 79Pテストを阻む要因を潰す テストに向かないアーキテクチャや道具を使う と対応コストを理由にやらないことが正当化さ れやすい。
  80. 80. 80Pテストを阻む要因を潰す テストできない理由が消えると、エンジニアは わりとちゃんとテスト書くようになる。 書いたほうがいいのは皆わかってるし、 書いたものは皆動かしたい。
  81. 81. 81Pテストを阻む要因を潰す アプリケーションアーキテクチャまで踏み込 んでテストを考慮することができればベスト。 ただこれが出来るのはかなり幸運なこと。
  82. 82. 未熟でも早めに適用、 フィードバックを受けて磨く
  83. 83. 83P早めに適用・フィードバックを受ける やり方を大幅に変えた時は最初からいろいろ頑 張りたくなる。 でもそこは一旦最低限に押さえて早く実戦投入 することが大事。
  84. 84. 84P早めに適用・フィードバックを受ける 机上で考えるよりも 実践の場で揉まれるほうが一番早い。
  85. 85. 85P早めに適用・フィードバックを受ける ただし最初の適用プロダクトでは途中で色々と 方針変更が入りがち。 それを承知してもらうのと、できればしがらみ の無い新規プロダクトがベスト。
  86. 86. 部門長サポートと仲間が大事
  87. 87. 87P部門長サポートと仲間が大事 現場で「こうしたい」という思いがあっても、 仕事で開発をしている以上はビジネス要件への 対応は一番力を割くべきところ。
  88. 88. 88P部門長サポートと仲間が大事 部門長や組織が • 攻めの開発(ビジネス要件対応) • 守りの開発(保守/刷新) の両方を理解してくれて初めて効果的に取り組める
  89. 89. 89P部門長サポートと仲間が大事 個人の能力とアウトプットはかなり限られる。 仲間がいれば、個の範囲を越えた成果が必ず出る。

×