Successfully reported this slideshow.
Your SlideShare is downloading. ×

Laravel.shibuya

Ad

バリデーション駆動開発(仮称)で
プロジェクトメンバー全員を幸せにした話
Laravel/オレオレ両対応

Ad

アジェンダ
話すこと
開発フェーズに特有の「厄介ななバグ」
「バリデーション」の適用範囲を考える
php/Laravel での実装例
テスト駆動開発との併用
話さないこと
Laravel のValidation

Ad

自己紹介
@KentarouTakeda
サーバサイド
php / node.js / TypeScript
Laravel / express / AWS Lambda
PostgreSQL / MySQL
インフラ
RHEL / CentO...

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Upcoming SlideShare
phpspecで始めるBDD
phpspecで始めるBDD
Loading in …3
×

Check these out next

1 of 60 Ad
1 of 60 Ad
Advertisement

More Related Content

Slideshows for you (19)

Advertisement

Laravel.shibuya

  1. 1. バリデーション駆動開発(仮称)で プロジェクトメンバー全員を幸せにした話 Laravel/オレオレ両対応
  2. 2. アジェンダ 話すこと 開発フェーズに特有の「厄介ななバグ」 「バリデーション」の適用範囲を考える php/Laravel での実装例 テスト駆動開発との併用 話さないこと Laravel のValidation
  3. 3. 自己紹介 @KentarouTakeda サーバサイド php / node.js / TypeScript Laravel / express / AWS Lambda PostgreSQL / MySQL インフラ RHEL / CentOS / AmazonLinux / Chef / Ansible フロントエンド TypeScript Angular / jQuery / Hexo
  4. 4. 心がけていること 担当業務の範囲にとらわれず、 幅広い視野で全体の効率アップを図りたい。 API / ネットワーク/ インターフェース 人/ 部署/ 会社
  5. 5. 「厄介なバグ」とは? 影響範囲が広い 再現が取れない 利用者への影響が大きい
  6. 6. 事例「ソシャゲのガチャ機能」 開発は「サーバ」「アプリ」「デザイナー」の3名 抽選はサーバ側の「ガチャAPI」が行う ガチャAPIの返却に応じてアプリ上で入手演出 素材は随時追加・デザイナー単独でcommit
  7. 7. バグ報告 「回す」ボタンを押した瞬間にクラッシュ。 演出まで進まない=画面には何の情報も無い 頻度は100回に1回程度。 外部スタッフからの報告 クラッシュログ入手困難 最初に誰に相談するか?
  8. 8. 考えられる原因 特定条件で「サーバ」が規格外のレスポンス 特定条件で「アプリ」が未定義プロパティを参照 特定の「素材」が破損している? 最初に誰に相談するか?
  9. 9. 些細なバグの裏で、こんなことが起きている? 「誰に相談すれば良いんだろう?」 「自分ではありません!」 「相談したら怒られた…」 「例のバグ、進捗どうですか?」 「全員に相談、するしかないな…」 「また人のバグで時間取られるのか…」
  10. 10. 「厄介なバグ」例えばこういう指針 困るのは誰か? 自分<自分以外の誰か 遭遇するのは誰か? 自分<自分以外の誰か 原因箇所を特定できるのは誰か? 自分以外の誰でも<自分のみ
  11. 11. 「厄介なバグ」をどう対処するか? 人海戦術で原因探し! 出さないように気をつける! あらかじめ可能性を潰しておく。
  12. 12. 今回はどこに問題があったのか?
  13. 13. 今回はどこに問題があったのか? 特定条件で「サーバ」が規格外のレスポンス というバグが存在したこと。
  14. 14. 今回はどこに問題があったのか? 特定条件で「サーバ」が規格外のレスポンス というバグが存在したこと。 というバグの可能性が存在したこと。
  15. 15. 可能性を潰す
  16. 16. IT用語辞典バイナリ バリデーション https://www.sophia-it.com/content/バリデーション 入力されたデータが、あるいはプログラミング言 語やマークアップ言語の記述が、規定された文法 に即して、または要求された仕様にそって、適切 に記述されているかどうかを検証することであ る。 “ “
  17. 17. バリデーションが必要な値とは? 外部から入力された値 信頼できない値
  18. 18. 信頼できない値
  19. 19. 自分の書くプログラムに 100%の信頼を持てますか?
  20. 20. JSON Schema https://json-schema.org/ JSONへの型定義 JSONが特定の型を満たしているか検証 JSON Schema is a vocabulary that allows you to annotate and validate JSON documents. “ “
  21. 21. ガチャAPI gatcha: # ガチャAPIは type: "object" # オブジェクトを返却する properties: cardIds: # cardIds プロパティは type: "array" # 配列型を取り items: type: "integer" # それぞれの値は整数 minimum: 1000 # 最小値は1000 maximum: 2999 # 最大値は2999 minItems: 1 # 配列の要素数は最小で1個 minItems: 3 # 最大で3個(1~3個のカードが出てくるガチャ) bonusId: # bonusId というプロパティは type: "integer" # 整数を取り minimum: 3000 # 3000以上 maximum: 4999 # 4999以下 required: # 必須プロパティは - "cardIds" # cardIds のみ(ボーナスは無い時もある) additionalProperties: false # 上記で定義された以外のプロパティは存在
  22. 22. PHP での実装 $ composer require justinrainbow/json-schema $validator = new Validator(); $validator->validate($response, $schema, $options); if(true !== $validator->isValid()) { throw new Exception("サーバ側のバグ!"); } echo json_encode($response); status=200 であれば必ず安全 安全でない場合必ず必ずstatus=500
  23. 23. 可能性を一つ潰した
  24. 24. 事例「Webアプリのマイページ」 Laravel案件、2名体制。 サーバエンジニア ルーティングやコントローラーを担当 Webデザイナー Bladeテンプレートへhtmlをコーディング
  25. 25. バグ報告 プロフィールに「氏名」を登録した状態でマイページ にアクセスすると以下のエラーが表示されます。 ErrorException (E_ERROR) Undefined variable: firstName (View: /var/www/resources/views/mypage/index.blade.php)
  26. 26. 考えられる原因 テンプレート側のtypo 正しくは$ rst_name だった コントローラー側のバグ $ rstName の初期化漏れ
  27. 27. 今回はどこに問題があったのか?
  28. 28. 今回はどこに問題があったのか? コントローラー側のバグ $ rstName の初期化漏れ というバグが存在したこと。
  29. 29. 今回はどこに問題があったのか? コントローラー側のバグ $ rstName の初期化漏れ というバグが存在したこと。 というバグの可能性が存在したこと。 ※コーダーはプログラマーの想像以上にエラー画面に ビビってます。
  30. 30. ビューに渡す値を検証 class MypageController extends Controller { public function index(Request $request) { /* ビューに渡す値を準備する */ /* ここでバリデーション? */ return view('mypage.index', $data); } } 何の保証にもならない。 (仕組み化して一括で防ぎたい)
  31. 31. コントローラーではオブジェクトを返 却するのみ class MypageController extends Controller { public function index(Request $request) { /* ビューに渡す値を準備する */ return $data; } }
  32. 32. ビュー適用 class ApplyViewMiddleware { public function handle($request, Closure $next) { $response = $next($request); /* 実際は abort() や RedirectResponse の際の処理が必要 */ $name = Route::currentRouteName(); // ビュー名 = ルート名 $response = new Response(view($name, $content)); return $response; } }
  33. 33. バリデーション class ValidationMiddleware { public function handle($request, Closure $next) { $response = $next($request); $name = Route::currentRouteName(); // バリデーション名 = ルート $isValid = $this->valication($response,$this->getSchema($nam if($isValid) { abort(500, "コントローラー側のバグ!") } else { return $response; } }
  34. 34. ミドルウェア適用 afterミドルウェアなので定義は逆順。 namespace AppHttp; use IlluminateFoundationHttpKernel as HttpKernel; class Kernel extends HttpKernel { protected $middlewareGroups = [ 'web' => [ /**/ AppHttpMiddlewareApplyView::class, // ビュー適用が先 AppHttpMiddlewareValidation::class, // バリデーションが後 ], ] }
  35. 35. バリデーションとテスト
  36. 36. テスト駆動開発(TDD) [DO03] 50 分でわかるテスト駆動開発 https://www.slideshare.net/decode2017/do03-50/9
  37. 37. TDDのサイクル 1. 次の目標を考える 2. その目標を示すテストを書く 3. そのテストを実行して失敗させる(Red) 4. 目的のコードを書く 5. 2.で書いたテストを成功させる(Green) 6. テストが通るままでリファクタリングを行う (Refactor) 7. 1~6を繰り返す
  38. 38. TDDと黄金の回転 Red, Grren, Refactor https://www.slideshare.net/decode2017/do03- 50/10
  39. 39. 出力の検証=テスト
  40. 40. バリデーション駆動開発 1. 次の目標を考える 2. その目標を示すテストバリデーションを書く 3. そのテストバリデーションを実行して失敗させる 4. 目的のコードを書く 5. 2.で書いたテストバリデーションを成功させる 6. テストが通るままでリファクタリングを行う 7. 1~6を繰り返す
  41. 41. 「ガチャAPI」
  42. 42. 目標「ガチャAPIが存在する」 // ApiTest.php class ApiTest extends TestCase { /** @test */ public function gatcha() { $this->post('/api/gatcha', [])->assertStatus(200); } }
  43. 43. テストをパスする最小限のコード # api.schema.yml gatcha: // ApiController.php class ApiController { public function gatcha() { } }
  44. 44. 次の目標「返却はcardIdsというプロ パティを持つ」 gatcha: + type: "object" + properties: + cardIds: class ApiController { public function gatcha() { + return [ + 'cardIds' => null, + ]; } }
  45. 45. 次の目標「cardIdsは配列」 gatcha: type: "object" properties: cardIds: + type: "array" class ApiController { public function gatcha() { return [ - 'cardIds' => null, + 'cardIds' => [], ]; } }
  46. 46. 次の目標「cardIdsの各要素(略)」 gatcha: type: "object" properties: cardIds: type: "array" + items: + type: "integer" + minimum: 1000 + maximum: 2999 + minItems: 1 + minItems: 3
  47. 47. class ApiController { public function gatcha() { return [ - 'cardIds' => [], + 'cardIds' => [1000, 1001, 1002], ]; } }
  48. 48. 開発体験 最小限のテストコード Red -> Green -> Refactor 仕様・実装・テストが同時に出来上がっていく 共有可能な成果物
  49. 49. 「ガチャAPI」
  50. 50. 今回はどこに問題があったのか?
  51. 51. 今回はどこに問題があったのか? 特定条件で「アプリ」が未定義プロパティを参照 というバグが存在したこと。
  52. 52. 今回はどこに問題があったのか? 特定条件で「アプリ」が未定義プロパティを参照 というバグが存在したこと。 というバグの可能性が存在したこと。
  53. 53. TypeScript https://github.com/bcherny/json-schema-to- typescript
  54. 54. { "title": "Example Schema", "type": "object", "properties": { "firstName": { "type": "string" }, "lastName": { "type": "string" }, "age": { "description": "Age in years", "type": "integer", "minimum": 0 }, "hairColor": { "enum": ["black", "brown", "blue"], "type": "string" } }, "additionalProperties": false,
  55. 55. export interface ExampleSchema { firstName: string; lastName: string; /** * Age in years */ age?: number; hairColor?: "black" | "brown" | "blue"; }
  56. 56. C# https://www.newtonsoft.com/jsonschema
  57. 57. { "$schema": "http://json-schema.org/draft-03/schema#", "title": "Country", "description": "A nation with its own government, occupying a "type": "object", "properties": { "flag": { "$ref": "flag.json" }, "cities": { "type": "array", "description": "A large town", "items": { "$ref": "city.json" }, "uniqueItems": true }, "population": { "type": "integer", "description": "The number of people inhabiting this count "minimum": 1000,
  58. 58. namespace generated { using System; using com.cvent.country.entities; using generated; using System.Collections.Generic; using Cvent.SchemaToPoco.Core.ValidationAttributes; using System.ComponentModel.DataAnnotations; // A nation with its own government, occupying a particular public class Country { // Used as the symbol or emblem of a country public Flag Flag { get; set; } // A large town public HashSet<City> Cities { get; set; } // The number of people inhabiting this country [Required()] [MinValue(1000)]
  59. 59. クライアントへの適用 ビルドが通っていれば確実に安全 安全でない場合ビルドで落ちる
  60. 60. 心がけていること 担当業務の範囲にとらわれず、 幅広い視野で全体の効率アップを図りたい。 API / ネットワーク/ インターフェース 業務/ 人/ 部署/ 会社 仕組み化で解決。 サーバサイドは仕組み化に取り組みやすい立ち位置。

×