「コミッターと読み進めるRailsリーディング会 #1」を開催しました!~ Rails v1.0.0を読み進める! ~

はじめに

はじめまして!オクトのRailsエンジニアの @KanechikaAyumu です!

弊社では、日々色々な勉強会が開催されています。

先日は、ANDPADの技術顧問をして頂いている松田さんにRailsリーディング会の勉強会を開催して頂きました!

prtimes.jp

貴重なお話を聞ける機会なので、社内の勉強会に閉じるのがもったいないと思い、外部の方も参加できるような形式を取らせて頂きました。当日参加してくださった方、平日の日中のお忙しい中にご参加頂き、ありがとうございました!!

oct.connpass.com

Railsソースコードを読んだことない人や、どこから読み進めて良いのか分からずな方は、参考にして頂ければと思います!

  1. 当日の様子
  2. Rails v1.0.0
  3. コミッター紹介
  4. Active Record
  5. Action Pack
  6. alias_method
  7. 最後に

当日の様子

当日は社内のエンジニアに加えて、数名外部のエンジニアの方にご参加頂きました。ご参加ありがとうございます!

松田さんの画面操作を見ながら、参加者全員でリーディングを行いました。Railsだけではなく、ソースコードの読み方やコマンド操作などたくさん学ぶことができました!

f:id:ayumu-kanechika:20200216175542j:plain

f:id:ayumu-kanechika:20200216175608j:plain


Rails v1.0.0

松田さんのオススメのOSSの読み方は、「First Commitから読み進めていくこと」とのことです。作者の本当に実現したかった内容が、ノイズ等がなく、シンプルに書き下されている為です。今回の勉強会でもv1.0.0から読み進めて行くことになりました。
 
皆さんもお手元にチェックアウトしてみて下さい!
$ git clone git@github.com:rails/rails.git
$ cd rails
$ git checkout v1.0.0
 

Railsは、フルスタックのMVCフレームワークです。
Mのモデルレイヤーは、O/Rマッパーも兼ねて「Active Record」が担います。
Cのベースとしては「「Action Pack」があり、当時は「ActionController」や「ActionView」が担っておりました。MVCの糊付けとして、「Railtise」があります。

勉強会では、中核な処理の「Active Record」「Action Pack」を読み進めました。

コミッター紹介 

その前にコミッター紹介です。生産者の顔が一番大事とのことです!

 

Railsは、DHHのファーストコミットで、大枠は出来上がっていました。
Action Mailer、Action Pack、Active Record、RailtiseなどMVCの中核の部分はだいたい全部入っていました。2004年にBase Camp(当時、37signals)のWebApplicationから切り出した為です。(とある1つ企業のフレームワークの切り出しで、この設計力の高さは驚愕です!!)
 
その最初のコミットがこちら
Initial
綺麗なコミットが積まれているのは、DHHがそういう性格の人だそうです。
 他にもコミットやRails Contributors をなどを見ながら、名前の挙がった方をつらつらと記載します!
$ git shortlog -sn db045dbbf6..v1.0.0
 
1771 David Heinemeier Hansson
278 Jeremy Kemper
205 Jamis Buck
79 Nicholas Seckar
77 Leon Breedt
70 Marcel Molina
42 Sam Stephenson
14 Thomas Fuchs
13 Tobias Lütke
12 Scott Barron
9 Florian Weber
8 Michael Koziarski
  1. DHH(David Heinemeier Hansson
    ファーストオーサー 37 signals CTO
    当時では珍しいRubyを使って、プロダクトを作り始めた。
    当時25歳。コードが書けて、設計できて、イケメン!!

  2. Jeremy Kemper
    No.2の重要人物
    Base Campにあとからジョインした。
    20世紀の頃からRubyを触っていた。
    Railsを影から支えるNo.2
    今の名前は、Jeremey Daer。
    結婚して、夫婦の名字を足して2で割ったらしい

  3. Jamis Buck
    Base campに後からジョイン
    代表作は、Switch Tower(?)。Rubyでピッとデプロイができる代物。
    商標などの兼ね合いで、のちにCapistranoに変わる。

  4. Nicholas Seckar
    主にActiveRecordを開発

  5. Marcel Molina
    当時は最年少。シンボルにProcを導入!

  6. SamStephenson
    まだ尚、社員
    JS系を色々と作っている。
    Asset Pipelineを作った人。Sprocketsも。

  7. Tobias
    社外の方。MySQLに強い。カナダ人。ActiveRecord
    Shopifyを起業をして、「CTO」ではなく「CEO」をやっている。

  8. Michael Koziarski
    ニュージーランドRailsコミッター。セキュリティ周り

  9. Eric Hodel
    シアトルRubyの親玉

  10. JoshPeek
    Railsコミッター。Rack。
    サンフランシスコの小さい企業Githubを立ち上げた。
    Rails魔改造をし続けて、Githubが2系を使い続けていた。

  11. Chat Fowler
     Rubyconf

  12. Shugo Maeda
    Railsのv1.0.0で唯一コミットが取り込まれた日本人

 

Active Record

当時のv1.0.0では「activerecord/lib/active_record/base.rb」の約1800行にコアの処理が全て書かれていました。他はおまけとのこと!
 
Railsも当時は新出のフレームワークだったので、ソースコードのコメントもかなり丁寧に書かれているので、キャッチアップしやすいです。
 
findメソッドにinteger渡すとプライマリキーとして検索されるなど、既に今と同じ形式でした。 機能としては、大枠できており、最先端という感じでした。最近の開発では、DSLが増えているという感じ。
 当時はfindしか使っておらず、relationとかなかったので、即SQL発行がされていました。
 
#find が面白いので、読み進めていきます!
def find(*args)
  ...
  case args.first
    when :first
      find(:all, options.merge(options[:include] ? { } : { :limit => 1 })).first
    when :all
      records = options[:include] ? find_with_associations(options) : find_by_sql(construct_finder_sql(options))
      ...
    else
      ...
      conditions = " AND (#{sanitize_sql(options[:conditions])})" if options[:conditions]
 
引数にfirst、all、それ以外の何を渡しても最終的に「find_by_sql」に「construct_finder_sql」が呼び出されています。
 
#construct_finder_sql はストレートに処理が書かれています!
def construct_finder_sql(options)
  sql = "SELECT #{options[:select] || '*'} FROM #{table_name} "
  add_joins!(sql, options)
  add_conditions!(sql, options[:conditions])
  sql << " GROUP BY #{options[:group]} " if options[:group]
  sql << " ORDER BY #{options[:order]} " if options[:order]
  add_limit!(sql, options)
  sql
end
 
#find_by_sql は今でも残る重要なAPIとのことです!
def find_by_sql(sql)
  connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) }
end
 
次に、#save を読み進めていきます。
def save
  raise ActiveRecord::ReadOnlyRecord if readonly?
  create_or_update
end
 
中では、#create#update を呼び出すことになり、そちらも愚直にSQLを構築しています!
def create
  if self.id.nil? and connection.prefetch_primary_key?(self.class.table_name)
    self.id = connection.next_sequence_value(self.class.sequence_name)
  end
  self.id = connection.insert(
    "INSERT INTO #{self.class.table_name} " +
    "(#{quoted_column_names.join(', ')}) " +
    "VALUES(#{attributes_with_quotes.values.join(', ')})",
    "#{self.class.name} Create",
    self.class.primary_key, self.id, self.class.sequence_name
  )
  @new_record = false
end
 本当にRailsのコアの処理はv1.0.0に詰まっています!
 
他に大事なのはなどがあるのですが、時間が足りずなので、次回以降のお楽しみになりました!
続きを読む

React Native経験者がFlutterをさわってみた


 はじめに

はじめまして、アプリチームの伊藤です。

オクトでは複数のアプリをリリースしていますが、その中でもReact Nativeを使っていたりFlutterを使っていたりと、同じクロスプラットフォーム開発ツールでも複数の技術が使われています。アプリチーム内でもよくこの話題があがっていたりして、もりあがっているテーマです。

そんな中、個人的にはReact Nativeか、Flutterかどちらかに注力して、ナレッジを蓄積したり、開発できるエンジニアを増やしたりすることも必要なのではないかと感じることがあります。そこで自分は前職でReact Nativeを使った開発をしていたこともあり、今回はFlutterをさわりながらその違いを学びつつ、今後の開発方針の検討材料にしていこうと思いました。 

 

2つのツールを比較する

まず、よくある2つのクロスプラットフォーム開発ツールのざっくりとした比較をまとめていきたいと思います。

 

x-plat tool React Native Flutter
開発者 Facebook Google
主な開発言語 JavaScript Dart
初版 2015年3月 2017年5月
IDE VS Code, Nuclide Android Studio, IntelliJ, VS Code
描画方式 ネイティブコンポーネントを使用 独自レンダリング

 

どちらも大手IT企業により開発されたツールですし、開発も活発なため今後のサポートに期待が持てます。開発言語や使えるIDEによってエンジニアの好みが分かれそうです。描画方式の違いはネイティブに近いアプリを作りたいか、プラットフォームが違っても差異が少ないUIで提供したいかなどでデザインの要望が関わってきそうです。

 

トレンドを見る

f:id:tomo-ito:20200209232443p:plain

React NativeとFlutterのトレンド

Google Trendsで日本での過去3年の動向をみてみました。React Nativeはあまり変化がないですが、Flutterは昨年の5月くらいから急上昇しています。Google IOのタイミングですね。人気というところではFlutterに軍配があがるようです。

 

サンプルを作って動かしてみる

技術は実際に自分の手で動かして確認することを信条としてますので、サンプルアプリを作りながら使い勝手などを試していきたいと思います。

 

CLIでプロジェクト作成・アプリ起動

環境構築については完了していることを前提とします。プロジェクト作成はIDEから行うやり方もありますが、シンプルなCLI操作による場合で両者を比較します。環境構築さえ済んでいれば、プロジェクト作成からシミュレータでのアプリ起動までスムーズに行えます。

 

React Native

$ npx react-native init react_native_sample_app
$ cd react_native_sample_app
$ npx react-native run-ios
または
$ npx react-native run-android

Flutter

$ flutter create flutter_sample_app
$ cd flutter_sample_app
$ flutter run

 

画像とテキストを表示するサンプルコード

アプリ起動の確認ができたら試し簡単なコードを書いてみます。React NativeではHTML・CSSライクなJSXで記述します。FlutterではWidgetツリーと呼ばれる階層構造で記述します。

 

React Native

import { AppRegistry, View, Image, Text, StyleSheet, } from 'react-native';
import React from 'react'; 
import {name as appName} from './app.json'; 

AppRegistry.registerComponent(appName, () => MyApp);

class MyApp extends React.Component {
  render() {
    return (
     <View style={styles.container}>
        <Image 
          style={styles.image}
          source={{uri: "http://bit.ly/39f0IkW"}}
        />
        <Text>明日から使えるカンタン施工管理アプリ</Text>
        <Text>by React Native</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center'
  },
  image: {
    width: 250, 
    height: 50
  }
});

Flutter

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      backgroundColor: Colors.white,
      body: Container(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <widget>[
              Image.network(
                "http://bit.ly/39f0IkW",
                width: 250,
                height: 50,
              ),
              Text("明日から使えるカンタン施工管理アプリ"),
              Text("by Flutter"),
            ]
          )
        )
      )
    )
  ));
}

 

結果

簡単なアプリなので見た目はほぼ同じです。

f:id:tomo-ito:20200210023739p:plainf:id:tomo-ito:20200210161030p:plain

 

補足

ここまで書いてアレですが、Flutterの公式サイトではReact Nativeとの違いについて丁寧なドキュメントを用意してくれておりとてもわかりやすいです。こういうところはとても好感が持てますね!

flutter.dev

 

さわってみての感想

  • FlutterはReactにインスパイアされて開発されたと言われているが、実際使ってみても共通する部分が多く、React Nativeでの開発経験がツール理解の助けになった。
  • 宣言的UIで記述し、かたやComponentを積み重ねて、かたやWidgetを積み重ねる。それぞれ用意されたコンポーネントの種類や使い方を覚える必要があって、肝心なハードウェアに近い部分はネイティブコードで記述し連携できる。
  • 後発のFlutterの方が洗練されているように感じた。JSXよりもWidgetツリーの方がスッキリしていて、CLI操作、静的型付け対応、IDEのバリエーション等から優れいている。
  • React NativeもTypeScript等を駆使してより良い環境を構築できるが、このあたりはWebフロント開発に慣れているエンジニアのみが力を発揮できる領域になる。

 

さいごに

 まだまだFlutterの勉強をはじめたばかりで、React Nativeに関しても経験豊富というわけでもない自分ですが、現在の印象としてはエンジニアの好みでどちらを使っても良いのかなと思いました。開発コストとしては大差なく、言語の普及率や学習コストなど勘案してもどちらが良いかの選択はエンジニアによってまちまちではないでしょうか。

アプリエンジニア(iOS/Android)ならFlutter、フロントエンドエンジニアならReact Nativeを選びそうです(バックエンドエンジニアならどちらだろう?)。

とはいえはじめに提起した課題の解消に向け、今後アプリチームでよく議論していくつもりです!

とある日の&ANDPAD開発部の1日

はじめに

はじめまして、開発アシスタントのしおざきです!

開発部はどんどん仲間が増え続け現在約60名。オクト内では大所帯です。
今回は、そんな開発部が日々どんな雰囲気で仕事をしているかお届けしたいと思います!

開発部の1日

10:00

大所帯の開発部ですが、この時間はまだひっそりしています。
集中できる時間に開発をしてもらうため、フレックス制を導入していますが、どうやら朝型はそんなに多くないようです。

 

11:00

この時間になるとだいぶ賑やかになります!

この日は週に一度の開発部会が開催される日。全員が集まると人数の多さを実感します。

 

f:id:shiiiio:20200203121214j:plain


 部会では、VPoEやVPoPがメンバーに伝えたい想いを話してくれます。
情報共有はもちろん、人が多くなると様々な考えや思いが交錯しがちですが、目指すべきところはみんなでひとつ、ということを改めて認識させてくれる場だったりもします。

 

12:00

ランチに出る人が多いこの時間帯。
特に時間が決まっているわけではないので、好きな時間にランチに行けます。

美味しかったお店などは「ここ行ったよ!」と共有しあったりするのですが、移転後によく目にするものが… 

 

f:id:shiiiio:20200130133045p:plain

メイド喫茶でお絵かきしてもらったオムライス。さすが秋葉原です。

 

13:00~15:00

ネタを探しにぷらりとしてみると、ミーティングをしているチームを発見。

f:id:yohei-fujii:20200203191707j:plain



真剣な様子が伺えます。

 

執務室内には大人数のMTGからちょっとした相談までできるフリースペースがたくさんありますが、気分転換したい時には近くのカフェに行くことも。

 

ということで、この日はCTOとカフェMTGをしてきました。

 

f:id:shiiiio:20200203144913j:plain


カフェが好きなCTOはいつもおしゃれなお店へ連れて行ってくれます。
エンジニアではなくても業務で困っていることなどをフランクに聞いてくれるので、とても心強い存在です。

 

16:00~18:00

作業に集中している人、気分転換に筋トレをする人、技術書を読む人など様々。
開発部にはたくさんの本があります。(恐らく社内でダントツの保持数)
そんな勉強熱心なメンバーはよく社内勉強会も開催しています。
勉強会の様子は記事になっているのでぜひ御覧ください!

 

tech.88oct.co.jp

tech.88oct.co.jp

tech.88oct.co.jp

 

19:00

平和な1日のようで、実は慌ただしい時間が流れていたりもする開発部。
この日も多忙を極めていたVPoE、なんとお誕生日ということが判明。忙しそうなので、みんなデスクの上にそっとプレゼントだけ置いて帰宅しました。

 

f:id:shiiiio:20200130131703j:plain

以上、とある日の開発部の1日でした。

おわりに

最後まで読んでいただきありがとうございます!
一緒に働く方募集中ですので、少しでも気になった方はこちらもぜひ!

engineer.88oct.co.jp

  

社内勉強会「開発におけるホスピタリティ~ コミットメッセージから始まる仲間の幸せ ~」を開催しました!

はじめに

はじめまして!オクトのRailsエンジニアの @KanechikaAyumu です!

創業期からいますが、最近は優秀な仲間がどんどん増えて、毎日、学びがたくさんです。最近は専らRailsのバージョンアップや、社内での技術基盤作りなどに取り組み、Railsと真摯に向き合っています!新規参画された方にオンボーディングの一貫で、過去のソースコードの背景や歴史を伝えていることも多いです!

 

先日、ANDPADの開発組織戦略顧問をして頂いている井原さんに開発部向けにチーム開発の真髄の勉強会をして頂きました!

lp.andpad.jp

そのレポートになります!

経緯

現在、オクトでは事業の急成長に伴い、新しい仲間もどんどん増えています!

チームで開発するにあたって、色々な課題が挙がっているのも事実です(今まで個人プレーになっていた部分も色々とあったので...)。

そんな中でも、1つの課題となっているGithub運用の事例などを交えて、チーム開発の真髄を教えて頂きました。 

内容

とある打ち合わせの場で、のりで決めてしまったタイトル「開発におけるホスピタリティ ~ コミットメッセージから始まる仲間の幸せ ~」で、発表を進めて頂き、ありがとうございました笑

コミットメッセージに関する内容は一部で、主にチーム開発におけるホスピタリティについて伝えて頂きました。

資料  

当日の様子

キャッチーなタイトルにしてたこともありw、当日は20、30人くらいの参加で、大盛況でした!サーバーサイド、フロントエンド、アプリエンジニア、SRE など、多くのエンジニアに参加して頂きました!

f:id:ayumu-kanechika:20200111200834j:plain

 f:id:ayumu-kanechika:20200111200832j:plain

ホスピタリティとは

ホスピタリティとは...「おもてなし」

おもてなしとは...「心のこもった待遇」

f:id:ayumu-kanechika:20200117103500p:plain

日々、一緒に働く周りの仲間に対して、「心のこもった待遇」をしていますか?という切り出しで始まりました。

 

自分がされたら嫌だなと思うことはしない

f:id:ayumu-kanechika:20200117103725p:plain

3ヶ月後には、今書いているソースコードなども忘れてしまうので、未来の自分も他人なので、自分もホスピタリティの対象になるとのことです。

意図がわかる

f:id:ayumu-kanechika:20200117103837p:plain

エンジニアであれば、Howはコードを見れば分かるので、なぜやっているかをちゃんと残していくことがとても大事だということです。

 

他人の時間をつくり出す

f:id:ayumu-kanechika:20200117104230p:plain

 

エンジニアは人の時間生み出すことができる職業で、めちゃくちゃ人手がいる作業も、時には1分、1秒で扱えるようにすることができる。

情報を圧縮して、再現して、コピーできるので、時には魔法とも言われる。

そんな時間を生み出す職業のエンジニアが、他人の時間を奪ってどうする!!というフレーズが強く胸に刺さりました。

 未来の自分のためにもホスピタリティあふれる開発をしていこうと思いました。

 

「ここから「じゃあ具体的にどうやるの?」が開始です」と、締めて頂きました。

全員で一緒によりよいチームを開発を考え、切磋琢磨していければと思います。

 

f:id:ayumu-kanechika:20200111200852j:plain 

最後に

勉強会の後は、みなさんのホスピタリティも高く、たくさん回答いただきました!!これがホスピタリティか!!!

f:id:ayumu-kanechika:20200117104749p:plain

f:id:ayumu-kanechika:20200117104653p:plain

 

お互いがお互いのことを考えられるホスピタリティ溢れる開発部にしていければと思っています。

自分たちでも考えながら、また顧問の方々から色々とご教示してもらいながら、全員でどんどん成長していければと思います! 

 

 

オクトのデザインチームを紹介するよ!

はじめに

初めましてデザインチームの杉本です。

 

オクトのデザイナーとして初めて書くことになったので、デザインチームの紹介をしていこうと思います。

デザインチームについて

デザイナーて何人います?

現在8人です。


1年前に3人でオクトのデザイン業務を回していたのでそこから考えると人数的に色々な事ができるようになって、機能も増えてきていて新しい事を考えたりブランディングにも力を入れ始めています。

 

   f:id:sugi0920:20200122111809p:plain

デザインチーム内の分担どうなっていますか?

基本的には、アプリやWebのUX/UIに関わっているデザイナーが6名、グラフィックデザインを主に行っているのが2名というふうに分かれています。


ここの分担は、曖昧で最近はブランディングが中心になっているデザイナーがいたりグラフィックからUI/UXの業務をやって貰ったりフロントエンドも兼任するデザイナーもいるのでやりたいことに手を挙げれば結構できる事が多いです。

新しいデザイナーが入ったらUI/UXとグラフィックが選べたりしますか?

基本的には別々に募集をかけているはずですが、両方ともに関わる事ができると思います。実際に人が少ない時にUI/UXもやりながらグラフィックのデザインもやってたので、UI/UXとグラフィック間の業務の行き来とかは簡単にできると思います。

 

開発体制について

デザインに使われているツールはなんですか?

Web・アプリのデザインはFigmaで、グラフィック関係はPhotoshopIllustratorで作るようになってます。
f:id:sugi0920:20200123104932p:plain

デザインする機能の分担はどうしています?

機能全てを改修することは少ないので何個かの機能を兼任するようになっています。僕の場合、新サービスやチャット、図面とかで要望とかが上がってきたり「新しい機能を追加するよー」となったら入っていくような感じになります。


 1から機能を開発する場合は、時間をさけるように兼任の他の機能のデザイン作業は少なくなっているような気もします。改修がある時はどうやっても上がってくるので並行して作業をすることも多いです。

 タスクなどの共有する時間はどうなっていますか?

毎週水曜日に自分たちが抱えているタスクを共有するミーティングをしています。最近は「Design doc」として要件を確認する時間を設けています。


さらに毎週金曜日にデザインのフィードバック会を行いデザインが要件を満たしているのかを確認するようになっています。

 

f:id:sugi0920:20200123112744j:plain

 

1人で作っていると気付けなかったことも他のデザイナーから意見が聞けるので前職で1人デザイナーやっていた時より気付きが多くて参考になります。

建築系のデザインする上で難しかったこととか面白いことありますか?

新しい知識を知るのは面白いです。工業高校の出なので建築とか工業の環境の方が近い環境だったので聞き覚えのある用語とか結構あったり、新築ができていく工程とかって自分が新築建てる予定がないと関わらないような知識が入ってくることなどです。

 

難しいことだとやっぱり情報量が多いことですかね。ANDPAD内には多くの機能があるので把握や用語を覚える必要があるので新入社員へのオンボーディングが課題として上がってきています。

uxmilk.jp

デザイナーはどの工程から入っていくことが多いですか?

僕の場合ですが基本的には要件がある程度できてから関わる場合が多い気がします。営業の方が利用中の会社からの要望を聞いている状態で入ることもあります。

開発部で自分たちがアプリを実際に使って欲しい機能を提案して作ったりすることもあるので、どこの工程から入っていっても要件に意見や感想を言いやすい環境ではあるので要件が決まっていても変えることもできると思います。

 

お客さんやエンジニアに見せるのはどのくらいの段階で見せますか?

お客さんに見せるのはデザイナー側でプロトタイプを見せることが多いです。機能のイメージを見て貰って実装前に意見を貰う事で手間のかかる実装後の改修を防ぐようにしています。

 

エンジニアの方に見せるのはデザインの方向性が決まってからです。そこで見せて意見や実装上難しい部分があればその意見を取り入れてデザインを作って改修したり、早めに見て貰っているとある程度の実装工数を見積もってくれるので調整が楽になりますね。

 

お客さんのところに行ったりとかしますか?

導入の説明会に行ったり遠方のお客さんだとビデオチャットで通話したりしています。最近、利用中の会社の方に協力していただいて開発から数人が現場に行ってどういった利用をしているかを実際に見せてもらいそれを撮影して社内に共有するという試みがされてました。

 f:id:sugi0920:20200123112623p:plain

 

普段なかなか見れない建築現場を見る事ができ、よりユーザーの理解ができる良い試みだと思います。

 

イベントとかって参加したりしてますか?

去年は希望者で「Designship」へ参加してます。色々なジャンルのデザインを見ることができたり有名なデザイナーの講演を聞くことができたのでいい経験でした。

あとは個人的に気になったイベントや展示会をデザインチーム内のチャットに共有しあったりしていますね。

おわりに

以上、今回はデザインチームの紹介をしました。最後まで読んで頂き、ありがとうございました!

少しでもご興味を持っていただけましたら、下記よりご連絡いただけますと幸いです!

 

hrmos.co 

テスト勉強会第5弾「自動テストの基礎」

はじめに

1月に入社したアプリチームの工藤です。

DeNA SWETグループの平田さんをお招きして、社内で第5回目のテスト勉強会を開催しました。この記事はそのレポートになります。

オクトでは定期的に社内勉強会を開催しています。

Vue.js勉強会や↓ tech.88oct.co.jp

SQL勉強会なども↓ tech.88oct.co.jp

今回は前回の反省を踏まえ、畳の小上がりスペースで行いました。
20名ほどの社員が真剣に耳をかたむけていて、雰囲気と場所のギャップが新鮮でした1

以前のテスト勉強会はこちら↓ tech.88oct.co.jp

内容

前半はユニットテストで重要な考え方であるFirst2とA-Rtip3に共通して挙げられている独立性・再現性について、後半は保守性の高いテストの設計について説明いただきました。

独立性

テストが増えるにつれ実行速度が課題になります。
改善するためには並列で稼働させる必要がありますが、テスト同士に依存関係がなく独立した状態でないといけません。

f:id:oct_k_kudo:20200117144203p:plain

これはテストをランダム実行することでチェックできますが、使える環境と使えない環境があるので注意が必要とのことです。

再現性

f:id:oct_k_kudo:20200117144243p:plain

CI環境は常に動くことが重要です。 たまに失敗したり再実行すれば成功するテストを放置していた結果、自動テストが信用されなくなってしまった現場を以前見たことがあり、この部分は当時を思い出しながら聞いていました。
再現性の低下に対処するには、可能性があるものをどれだけ知っているか?がカギになるそうです。

保守性について

f:id:oct_k_kudo:20200117145547p:plain

テストコードが徐々に負債になるというのはあまり意識したことがなく、非常に勉強になりました。
レガシー化した部分の改善を容易にするためにも、シンプルなテストコードを意識するのが重要とのことです。 書き方の参考として、 AAA・Four-Phase Testというパターンを紹介いただきました。

質問タイム(特に印象に残ったもの)

Q. テストケース名に日本語を使うか?

A 無理に英語にする必要はないが、日本語はプラットフォームや環境によっては正しく実行できないことがあるので確認してから使う。
AndroidではJunit5からは @DisplayName が使えるので活用すると良いと後から教えていただきました。

Q. 既存コードに対するテスト追加とテストのためのリファクタリングはどちらを優先すべきか

A リファクタすべきという危険を感じ取ったら(スライドの表現を使って匂いと表現されてました)、まずはリファクタリングすべき。
リファクタ前に無理やりテストコードを書いたとしても、リファクタ後にそれが負債になってしまう。

最後に

テストカバレッジなどは元々知識として知っていたものの、テストコードの保守性のために設計が必要という事に気づけたのが良かったです。 今はモバイルアプリのCD/CI環境整備を進めていて、自動テストのコードも徐々に増えてきています。
私も早くテストコードの匂いを感じ取れるように成長していきたいです。

こちらのサイトもよろしくおねがいします!

engineer.88oct.co.jp


  1. 写真を撮り忘れてました。。。

  2. 「Clean Code アジャイルソフトウェア達人の技」

  3. 「達人プログラマー―ソフトウェア開発に不可欠な基礎知識」

ベタなiOSアプリのCI/CDのワークフローを組む

こんにちは。モバイルアプリの開発を担当をしているzigeninです。

ANDPADのiOSアプリのCI/CDワークフローを紹介します。 ベタなツールにベタなワークフローですので、他のiOSアプリにも適用できると思います。 一例として参考になればと思います。

目次

事前情報

ビルドに使用しているツール

ツール 用途
Bitrise 特定のブランチにpushした時にワークフローを実行
Firebase App Distribution アプリの配布
fastlane matchでアプリの署名用のcertificates & provisioning profileを取得
pod 依存ライブラリを入手

iOSアプリのビルドScheme

Scheme 用途
andpad Release版アプリをビルドする。
andpadUITests Release版アプリで、安定したUIテストのみ実行する用。
andpad_develop Develop版アプリをビルドする。Unitテスト実行用でもある。
andpadUITestsDevelop Develop版アプリで、安定したUIテストのみ実行する。
andpadNightlyUITests 全UIテストを実行する。

ANDPADではビルドSchemeによって、普段の開発に使うDevelop版アプリと、Storeに公開する用のRelease版アプリを分けています。 Develop版アプリは開発用アプリで、developサーバと接続しています。 Release版アプリはStoreに公開するアプリと同一で、本番サーバに接続しています。

UIテストのSchemeを3つ分けていますが、状況によってUIテストの実行の仕方を変えるためです。 UIテストをCI/CDのワークフローに組み込むための肝です。 詳細は、後述のNightly UI Testのワークフローで説明します。

ブランチ戦略

大体、GitHub Flowです。

ブランチ 用途
master Storeに公開したアプリのコードの置き場所
develop 開発本線。最新のコードの置き場所
feature/* 機能開発用ブランチ

運用図は、ワークフローの全体像と一緒に示します。

ワークフローの全体像

GitHubのブランチをベースに、ワークフローを示します。

f:id:zigenin:20200114160516p:plain:w640
ブランチ戦略とワークフローの関係性

主要なワークフローは3つです。

  • 内部配信のワークフロー
    • 用途
      • Develop版アプリの自動テストを実施 + 社内にアプリを配布する
    • トリガー
      • ブランチをdevelopにマージした時
    • やること
      • ビルドの準備をする
        • コードのチェックアウト
        • 証明書をfastlane matchで入手
        • 依存ライブラリをpodで入手
      • Unitテストを実行する
      • アプリをビルドする
      • ビルドしたアプリをFirebase App Distributionに配布する
      • 安定しているUIテストを実行する*1
      • ビルドの後始末をする
        • テスト結果を保存する
        • Slackにビルド結果を通知する
  • Store配信ワークフロー
    • 用途:Release版アプリの自動テストを実施 + Storeにアップロードする
    • トリガー:developブランチをmasterブランチにマージした時*2
    • やること(内部配信フローと大体同じ。違いは太字で強調)
      • ビルドの準備をする
      • Unitテストを実行する
      • アプリをビルドする
      • ビルドしたアプリをStoreに配布する
      • 安定しているUIテストを実行する
      • ビルドの後始末をする
  • Nightly UI Testワークフロー
    • 用途:追加したばかりのUIテストが安定していることを確認する
    • トリガー:毎日定刻に、developブランチに対して実行
    • やること(内部配信フローと大体同じ。違いは太字で強調)
      • ビルドの準備をする
      • Develop版アプリで 全てのUIテストを実行する
      • ビルドの後始末をする

以降、この3つのワークフローとそれらを構成するユーテリティワークフローを、Bitriseでどのように設定しているかを紹介します。

ユーティリティワークフロー

先程示した3つのワークフローには、共通部分が多いです。 共通部分は、4つのBitriseのユーテリティワークフローとしてまとめています*3

  1. ビルドの準備(_BeforeCommonWorkflow)
  2. Unitテスト実行&アプリのビルド(_BuildAndUnitTest)
  3. UIテスト実行(_UITest)
  4. ビルドの後始末(_AfterCommonWorkflow)

ワークフロー名の先頭に"_"を付けると、ユーティリティワークフローとなります。ユーティリティワークフローにすると、他のワークフローの部品として扱われ、独立して実行することができなくなります*4

4つのユーテリティワークフローの設定の詳細を紹介します。

ビルドの準備(_BeforeCommonWorkflow)

f:id:zigenin:20200114003504p:plain:w240
_BeforeCommonWorkflowを構成するStep

  • Activiate SSH Key
  • Git Clone Repository
  • Bitrise.io Cache:Pull
  • fastlane match
    • Develop版アプリの署名用のcertificates & provisioning profileを取得するStep*5
    • "fastlane lane"に"match development"を設定
    • fastlaneに必要なFASTLANE_USER, MATCH_PASSWORD, FASTLANE_PASSWORDは、BitriseのSecretsに設定
  • fastlane match
    • リリース版アプリの署名用のcertificates & provisioning profileを取得するStep*6
    • "fastlane lane"に"match appstore"を設定
  • Run CocoaPods install

このワークフローは、ワークフローごとにパラメータや処理に違いはありません。

Unitテスト実行&アプリのビルド(_BuildAndUnitTest)

f:id:zigenin:20200114115350p:plain:w240
_BuildAndUnitTestのStep

  • Set Xcode Project Build Number
    • Bitriseのビルド番号をアプリのBundle Versionとして設定するStep
    • 配布したアプリと対応するBitriseのビルドが分かりやすくなり、問題が起きた時の追跡などの時に便利です
    • "Info.plist file path"に"$BITRISE_SOURCE_DIR/$INFO_PLIST_PATH"を設定
      • AndpadではDevelop版とRelease版でInfo.plistが違うので、フローごとに切り替える必要がある
  • Xcode Test for iOS
    • Unit Testを実行するStep
    • Schemeとして、"andpad_develop"を設定
    • Simulator Deviceには、"iPhone Xs Max"を設定
  • Xcode Archive & Export for iOS
    • アプリをビルドするStep
    • "Rebuild from bitcode"を"no"に設定
      • export methodが"development"の時にbitcodeを生成するかのオプション
      • 內部配信の場合、bitcodeをリビルドする意味があまりない*7 かつ ビルド時間短縮のために、Offにしている
    • "Select method for export"に"$BITRISE_EXPORT_METHOD"を設定
    • "Configuration name"に"$BUILD_CONFIG"を設定
    • "iCloud container environment"に"$ICLOUD_CONTAINER_ENVIRONMEN"を設定
  • IPA info
    • ビルドしたアプリの情報をBitriseの環境変数に設定してくれるStep
    • ここで設定される$IOS_IPA_PACKAGE_NAME(値はBundle Identifier)をビルド結果をSlackに通知する時に使う

UIテスト実行(_UITest)

f:id:zigenin:20200114131312p:plain:w240
_UITestを構成するStep

  • Xcode Build for testing for iOS
    • UIテスト用のアプリをビルドするStep
    • "Configuration name"に"$BUILD_CONFIG"を設定
    • "Scheme name"に"$BITRISE_UITEST_SCHEME"を設定
  • iOS Device Testing
    • UIテストをFirebase Test Labで実行するStep
    • "Test devices"に"iphonexs,12.3,ja,portrait"を設定(iPadや複数OSバージョンをTestするようにする予定です))

ビルドの後始末(_AfterCommonWorkflow)

f:id:zigenin:20200114005906p:plain:w240
_AfterCommonWorkflowを構成するStep

  • Bitrise.io Cache: Push
  • Deploy to Bitrise.io - Apps, Logs, Artifacts*8
    • Slackで結果を通知しているのでEmail通知は無効化
      • "Notify: User Roles"を"none"に設定
  • Send a Slack message
    • ワークフローごとに通知先のチャンネルを切り替えられるよう設定
      • "Slack API token"に$SLACK_API_TOKENを設定($SLACK_API_TOKENの値はBitriseのSecretsで設定)
      • "Target Slack channel"に$SLACK_TARGET_CHANNELを設定
        • Web Hook URLを使わないのは、Web Hook URLだとチャンネルの数だけSecrets変数が設定が必要なため
    • SlackのメッセージにBundle IDを含めるように設定
      • "A list of fields to be displayed in a table inside the attachment"に次の内容を記述。
      • App|${BITRISE_APP_TITLE}
        BundleID|${IOS_IPA_PACKAGE_NAME}
        Branch|${BITRISE_GIT_BRANCH}
        Workflow|${BITRISE_TRIGGERED_WORKFLOW_ID}

      • BundleIDを含めるのは、ビルドされたのがDevelop版なのかRelease版なのかを区別するため

内部配信ワークフロー

Steps

f:id:zigenin:20200114161954p:plain:w240
內部配信ワークフローを構成するStep

ワークフローの大まかな構成を示します。 - BeforeCommonWorkflow - BuildAndUnitTest - 內部配信ワークフロー本体のStep - UITest - AfterCommonWorkflow

長く見えますが、ほぼ前述のユーテリティワークフローで構成されています。 このワークフロー本体のStepは、Firebase App Distributionのみです。

  • Firebase App Distribution*9
    • ビルドしたアプリをFirebase App Distributionに配布するStep
    • "Firebase Token"には、Firebaseの認証トークンを設定
    • "Release Notes"には以下を設定
      • $GIT_CLONE_COMMIT_MESSAGE_SUBJECT

        $GIT_CLONE_COMMIT_MESSAGE_BODY

    • "Test Groups"には、Firebase App DistributionのTester Groupを設定
    • "Firebase App ID"には、Firebase上のアプリのIDを設定

環境変数の設定

本体のStepが少ない分、環境変数の設定が重要になるので、示しておきます。

環境変数 用途 使っているワークフロー
BITRISE_PROJECT_PATH xcworkspaceファイルの場所を指定 andpad.xcworkspace BuildAndUnitTest, UITest
BITRISE_SCHEME アプリのビルドSchemeを指定 andpad_develop _BuildAndUnitTest
BITRISE_EXPORT_METHOD アプリのビルド時のExport Methodを指定 development _BuildAndUnitTest
SLACK_TARGET_CHANNEL ビルド結果の通知先のSlackチャンネル名 #andpad-app-build _AfterCommonWorkflow
ICLOUD_CONTAINER_ENVIRONMENT アプリビルド時のiCloud Containerを指定 Development _BuildAndUnitTest
BITRISE_UITEST_SCHEME UIテストアプリのビルドSchemeを指定 andpadUITestsDevelop _UITest
BUILD_CONFIG アプリのビルドconfigを指定 Debug BuildAndUnitTest, UITest
INFO_PLIST_PATH Info.plistの場所を指定 andpad-develop-Info.plist _AfterCommonWorkflow

Store配信ワークフロー

Steps

f:id:zigenin:20200114172628p:plain
Store配信ワークフローを構成するStep

ワークフローの全体構成を示します。 - BeforeCommonWorkflow - BuildAndUnitTest - Store配信ワークフロー本体のStep - UITest - AfterCommonWorkflow

このワークフロー本体のStepは、Deploy to iTunes Connectのみです。

  • Deploy to iTunes Connect
    • ビルドしたアプリをStoreに配布するStep
    • "Apple ID"に、Storeにアップロードする権限のあるユーザIDを設定
    • "Password"には、Storeにアップロードする権限のあるユーザのパスワードを設定
    • "Team ID"には、iTunes ConnectのアプリのTeam IDを設定
    • "App Bundle ID"には、アプリのBundle IDを設定。
      • _BuildAndUnitTestのStep "IPA Info"のおかげで$IOS_IPA_PACKAGE_NAMEが使えるので、それを設定

環境変数の設定

本体のStepが少ない分、環境変数の設定が重要になるので、示しておきます。

環境変数 用途 使っているワークフロー
BITRISE_PROJECT_PATH xcworkspaceファイルの場所を指定 andpad.xcworkspace _BuildAndUnitTest, _UITest
BITRISE_SCHEME アプリのビルドSchemeを指定 andpad _BuildAndUnitTest
BITRISE_EXPORT_METHOD アプリのビルド時のExport Methodを指定 app-store _BuildAndUnitTest
SLACK_TARGET_CHANNEL ビルド結果の通知先のSlackチャンネル名 #andpad-app-build _AfterCommonWorkflow
ICLOUD_CONTAINER_ENVIRONMENT アプリビルド時のiCloud Containerを指定 Production _BuildAndUnitTest
BITRISE_UITEST_SCHEME UIテストアプリのビルドSchemeを指定 andpadUITests _UITest
BUILD_CONFIG アプリのビルドconfigを指定 Release _BuildAndUnitTest, _UITest
INFO_PLIST_PATH Info.plistの場所を指定 andpad/Info.plist _AfterCommonWorkflow

Nightly UI Testワークフロー

Steps

f:id:zigenin:20200114181600p:plain
Nightly UI Testワークフローを構成するStep

ワークフローの全体構成を示します。 - BeforeCommonWorkflow - UITest - _AfterCommonWorkflow

このワークフローは、本体のStepはなしです。

環境変数の設定

環境変数の設定は內部配信のワークフローとほぼ同じです。違いだけ示します。

環境変数 用途 使っているワークフロー
BITRISE_UITEST_SCHEME UIテストアプリのビルドSchemeを指定 andpadNightlyUITests _UITest
SLACK_TARGET_CHANNEL ビルド結果の通知先のSlackチャンネル名 #team-qa-mobile-cicd _AfterCommonWorkflow
IOS_IPA_PACKAGE_NAME Slackで通知のアプリのBundle ID jp.reformpad.ios.develop _AfterCommonWorkflow

Slackの通知先のチャンネルを、弊社のアプリのCI/CDを整備する活動のチャンネルにしています。 定期実行のテスト結果を、配信用のチャンネルに通知すると、配信の情報が埋もれるためです。

補足

このワークフローは、追加したばかりのUIテストの安定性を確認するために、毎日定時に実行しています。

UIテストはUnitテストに比べると不安定です*10。 そのため、最初から內部配信ワークフローやStore配信ワークフローに組み込んでしまうと、ビルドエラーの頻度が上がります。 そうなってしまうと、UIテストのせいで却って開発効率が落ちてしまいます。

そこで、毎日定時にUIテストを実行し、連続で7回成功したら、配信ワークフローに組み込む運用にしています。 それであれば、配信ワークフローで実行するUIテストを安定したものだけに限定できます*11。 全UIテストを実行するSchemeを"andpadNightlyUITests"と、配信ワークフロー用のUI TestのSchemeを"andpadUITests", "andpadUITestsDevelop"に分けることで、この運用を実現しています。

参考情報

参考書籍

本ワークフローを構築にするにあたって、以下の書籍の「9章 CI/CD」を参考にしています*12

iOSテスト全書

ここで紹介したワークフローは、書籍で紹介されているワークフローを大分デチューンしています。 もっとしっかりしたワークフローを組みたいという方は、この本がおすすめです。テストやCI/CDのそもそも論も学べます。

Bitriseの設定のソースコード

---
format_version: '8'
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
project_type: ios
trigger_map:
- push_branch: develop
  workflow: InternalRelease
- push_branch: master
  workflow: Release
- push_branch: test/*
  workflow: RunAllUITests
workflows:
  InternalRelease:
    steps:
    - firebase-app-distribution@0.5.0:
        inputs:
        - firebase_token: "$FIREBASE_TOKEN"
        - release_notes: |-
            $GIT_CLONE_COMMIT_MESSAGE_SUBJECT

            $GIT_CLONE_COMMIT_MESSAGE_BODY
        - groups: Andpad
        - app: "$FIREBASE_APP_ID"
    before_run:
    - _BeforeCommonWorkflow
    - _BuildAndUnitTest
    after_run:
    - _UITest
    - _AfterCommonWorkflow
  Release:
    steps:
    - deploy-to-itunesconnect-deliver@2.17.0:
        inputs:
        - itunescon_user: "<アプリのアップロードする権限を持つユーザID>"
        - team_id: <アプリのTeamID>
        - bundle_id: "$IOS_IPA_PACKAGE_NAME"
        - password: "<アプリのアップロードする権限を持つユーザのパスワード>"
    envs:
    - opts:
        is_expand: false
      BITRISE_SCHEME: andpad
    - opts:
        is_expand: false
      BITRISE_EXPORT_METHOD: app-store
    - opts:
        is_expand: false
      APP_BUNDLE_ID: jp.reformpad.ios.production
    - opts:
        is_expand: false
      FASTLANE_MATCH_TYPE: appstore
    - opts:
        is_expand: false
      ICLOUD_CONTAINER_ENVIRONMENT: Production
    - opts:
        is_expand: false
      BITRISE_UITEST_SCHEME: andpadUITests
    - opts:
        is_expand: false
      BUILD_CONFIG: Release
    - opts:
        is_expand: false
      INFO_PLIST_PATH: andpad/Info.plist
    before_run:
    - _BeforeCommonWorkflow
    - _BuildAndUnitTest
    after_run:
    - _UITest
    - _AfterCommonWorkflow
  _BeforeCommonWorkflow:
    steps:
    - activate-ssh-key@4.0.5:
        run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}'
    - git-clone@4.0.17: {}
    - cache-pull@2.1.3: {}
    - fastlane@2.7.0:
        inputs:
        - lane: match development
        title: fastlane match
    - fastlane@2.7.0:
        inputs:
        - lane: match appstore
        title: fastlane match
    - cocoapods-install@1.10.0: {}
  _AfterCommonWorkflow:
    steps:
    - cache-push@2.2.3: {}
    - deploy-to-bitrise-io@1.9.5:
        inputs:
        - notify_user_groups: none
    - slack@3.1.3:
        inputs:
        - channel: "$SLACK_TARGET_CHANNEL"
        - fields: |
            App|${BITRISE_APP_TITLE}
            BundleID|${IOS_IPA_PACKAGE_NAME}
            Branch|${BITRISE_GIT_BRANCH}
            Workflow|${BITRISE_TRIGGERED_WORKFLOW_ID}
        - api_token: "$SLACK_API_TOKEN"
    before_run: []
  _BuildAndUnitTest:
    steps:
    - set-xcode-build-number@1.0.9:
        inputs:
        - plist_path: "$BITRISE_SOURCE_DIR/$INFO_PLIST_PATH"
    - xcode-test@2.4.3:
        inputs:
        - generate_code_coverage_files: 'yes'
        - scheme: "andpad_develop"
        - simulator_device: iPhone Xs Max
    - xcode-archive@2.7.1:
        inputs:
        - compile_bitcode: 'no'
        - export_method: "$BITRISE_EXPORT_METHOD"
        - configuration: "$BUILD_CONFIG"
        - icloud_container_environment: "$ICLOUD_CONTAINER_ENVIRONMENT"
    - ipa-info@1.1.0: {}
    before_run: []
    after_run: []
  _UITest:
    steps:
    - xcode-build-for-test@0.4.0:
        inputs:
        - configuration: "$BUILD_CONFIG"
        - scheme: "$BITRISE_UITEST_SCHEME"
    - virtual-device-testing-for-ios@0.9.10:
        inputs:
        - test_devices: iphonexs,12.3,ja,portrait
    before_run: []
    after_run: []
  NightlyUITests:
    envs:
    - opts:
        is_expand: false
      SLACK_TARGET_CHANNEL: "#team-qa-mobile-cicd"
    - opts:
        is_expand: false
      BITRISE_UITEST_SCHEME: andpadNightlyUITests
    - opts:
        is_expand: false
      IOS_IPA_PACKAGE_NAME: jp.reformpad.ios.develop
    before_run:
    - _BeforeCommonWorkflow
    - _UITest
    after_run:
    - _AfterCommonWorkflow
app:
  envs:
  - opts:
      is_expand: false
    BITRISE_PROJECT_PATH: andpad.xcworkspace
  - BITRISE_SCHEME: andpad_develop
    opts:
      is_expand: false
  - opts:
      is_expand: false
    BITRISE_EXPORT_METHOD: development
  - opts:
      is_expand: false
    SLACK_TARGET_CHANNEL: "#andpad-app-build"
  - opts:
      is_expand: false
    ICLOUD_CONTAINER_ENVIRONMENT: Development
  - opts:
      is_expand: false
    BITRISE_UITEST_SCHEME: andpadUITestsDevelop
  - opts:
      is_expand: false
    BUILD_CONFIG: Debug
  - opts:
      is_expand: false
    INFO_PLIST_PATH: andpad-develop-Info.plist
  - opts:
      is_expand: false
    FIREBASE_APP_ID: "<FirebaseのアプリのID>"
meta:
  bitrise.io:
    machine_type: elite

*1:アプリ配布後にUI Testを実行させているのは、アプリ外の要因でUIテストが失敗することがあるためです。テストが失敗した時、ログを見て外部要因であれば、アプリの再配信はしない運用にしています。

*2:masterにマージする前に、Develop版アプリを使って手動テストを実施しています。それで問題なければ、masterにマージしています。

*3:詳細は https://devcenter.bitrise.io/bitrise-cli/workflows/#utility-workflows を参照

*4:これがユーテリティワークフローの良い所です。手動でビルドを開始する時などにワークフローリストに表示されなくなるので見通しが良くなります。

*5:fastlane matchを使っているのは、Bitrise導入前からfastlane matchでcertificates & provisioning profileを管理していたのを踏襲したからです。

*6:Develop版とリリース版アプリの両方のcertificates & provisioning profileが必要になるケースがある

*7:bitcodeは、Storeにアップロードした時に、Apple側で最適化をするためのもの。

*8:iOSテスト全書の事後ワークフローに含まれていませんが、個人的には含めておいた方が良いと思います。Unitテストの結果がBitriseのTest Reportで見れる、ビルドログやビルドしたアプリがArtifactに保存される、BitriseのSlackやメールの通知からアプリをインストールできるなどの利点があります。

*9:設定の意味の詳細は、 https://firebase.google.com/docs/app-distribution/ios/distribute-cli?hl=ja を参照。

*10:通信が発生する、OSバージョン、Deviveの置かれた環境に依存するためです。弊社固有の事情ですが、最近まで弊社にモバイルアプリのUIテストのノウハウがなかったことも原因です

*11:なお、OSのバージョンアップ等で、配信ワークフローに組み込んだUIテストが不安定になってしまった場合は、配信ワークフローから外す運用です

*12:弊社のテスト面の技術顧問の平田さんが記述しています