こけこっこー

がんばる

開発期間見積もりとバッファ

アプリ開発のプロジェクトマネージャーとして、開発者が開発期間の見積もりの際にバッファをきちんと取らなかったためにスケジュールが遅延するという状況を最近見たので、我々は開発期間見積もりの際に何を考慮していて、何をバッファに詰め込んでいるのか、ということを考えました。

前提として、我々のチームではアジャイル開発を行っていて、スクラム開発でしばしば行われるようなイベントもいくつか取り組んでいます。 また、開発工数ではなく、開発期間という言葉を使っていますが、これはプロダクトに関する実作業だけではなく、その他の要素も含めた、開発期間の見積もりについての検討なのでこのように表現しています。

まずは我々のチームのアプリ開発者が、ある機能を開発している最中に発生する作業を洗い出すとこんな感じです。(すべてがいつも発生する作業ということではないですが)

開発

  • 技術調査
  • システム設計
  • 実装(Pull Request出すとこまで)
  • 動作確認
  • Pull Requestに対するレビュー指摘事項の修正
  • 不具合修正
  • 他のメンバーのコードレビュー

コミュニケーション

  • ミーティング
  • チームメンバーのヘルプ
  • チーム外への報告、連絡、相談

キャッチアップ

  • 休暇など、不在期間のキャッチアップ
  • 使用する技術の習得
  • チーム・開発方針への順応

プロジェクト進行

  • タスク整理
  • プロセス改善

運用

  • 問い合わせ対応
  • エラー原因調査
  • チーム外部からの質疑応答
  • ドキュメント作成

また、開発期間増大の要因を洗い出すとこんな感じです。

  • 詳細要件待ち
  • デザイン待ち
  • 他者担当の開発待ち
  • 要件漏れ・追加・変更
  • 隠れタスクの発見
  • 対象範囲の拡大

これら以外にも細々とした作業や、期間増大要因はありますが、パッと思いつく限りで書き出してみてもこれだけのものがあります。 そしてこれらの中で、見積もりの際になんとなく頭に思い浮かんでいる作業は見積もりに含まれていて、それ以外の作業と開発期間増大要因はバッファに含まれていると思います。

恐ろしいです...

私が開発者として期間を見積もる際、思い浮かんでいたのは「開発」のカテゴリに挙げたいくつかの作業だけだったような気がします。 それ以外はすべてバッファですが、バッファをたくさんとるのはなんとなく後ろめたい気持ちでした。 ですが、実際にはバッファにはとても多くの作業と開発期間増大要因が含まれていたのです。

ちゃんとバッファを取りましょう。 あと、チームで見積もりを行う際には、どの作業を見積もりに含め、どの作業をバッファに詰め込むのかを話し合いで統一しておくのが良さそうです。

React Native ElementsのButtonコンポーネントで何故か隙間ができるときの対処法

React Native ElementsのButtonコンポーネントを使っていたところ謎のpadding的な隙間ができて困りました。 こんな感じです。

f:id:monisoi:20180622071332p:plain

  nextButtonWrapper: {
    flex: 1,
    height: '70%',
    backgroundColor: '#CCC3BA',
  },
  nextButton: {
    height: '100%',
    width: '100%',
    backgroundColor: 'steelblue',
  },
const renderNextButton = dispatch => (
  <View style={styles.nextButtonWrapper}>
    <Button onPress={() => dispatch({ type: GO_TO_NEXT_TURN })} title="次へ" buttonStyle={styles.nextButton}/>
  </View>
);

青と灰色をすべて含む部分がViewコンポーネントnextButtonWrapper のスタイルを適用してます。 その中にButtonコンポーネントを入れていて nextButton のスタイルを適用してます。 nextButton はwidth, heightどちらも100%を指定しているので青い部分が灰色の部分まで広がることを期待しています。 つまりボタン押しできる範囲をviewコンポーネントの範囲全体に広げたいのです。

結局Buttonコンポーネントではうまく行かなかったのでReact Nativeの TouchableOpacity を使いました。 TouchableOpacity これで範囲全体をクリックできるようになります。

  nextButton: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    height: '70%',
    backgroundColor: '#CCC3BA',
  },
const renderNextButton = dispatch => (
  <TouchableOpacity style={styles.nextButton} onPress={() => dispatch({ type: GO_TO_NEXT_TURN })}>
    <Text>次へ</Text>
  </TouchableOpacity>
);

React Nativeでstyleのオブジェクトを統合する

ReactNativeではstyleをStyleSheet.create()して作ります。 以下のようなstyleを作りました。

const styles = StyleSheet.create({
    styleA: {
      width: '30%',
      height: '42%',
    },
    styleB: {
      backgroundColor: '#CCC3BA',
    }
});

styleAstyleB を統合させたくて以下のようにやったのですが、エラーになってしまいました。

const styleAB = {...styles.styleA, ...styles.styleB};

そもそも styles.styleAStyleSheet.create() によって生成されているので、出力がnumberになってしまうようです。 なので、上記のようなことがしたければ、 StyleSheet.flatten() を使う必要があります。

const styleAB = StyleSheet.flatten([styles.styleA, styles.styleB]);

ReactNativeでstyleをグローバルに設定する

アプリ全体のText Componentのcolorを一括指定してしまいたい、というときのやり方。 公式で推奨されているやり方によると、アプリ専用のコンポーネントを作成してそれを使いなさい、ということみたいです。

facebook.github.io

具体的には /custom/Text.js みたいなファイルを用意して、以下のように書けば良さそうです。

import React, { Component } from 'react';
import { Text, StyleSheet } from 'react-native';

const styles = StyleSheet.create({
  appText: {
    fontSize: 16,
    color: '#66605D',
  },
});

export class AppText extends Component {
  render() {
    return (
      <Text {...this.props} style={[styles.appText, this.props.style]}>
        {this.props.children}
      </Text>
    );
  }
}

実際に使用する側では、 import { Text } from 'react-native'; を書き換えて import { AppText } from '../custom/Text'; みたいに読み込んでやれば良いです。

ReactNativeElementsでButtonにiconを使う

ReactNativeElementsでボタンにアイコンを入れようと思ったらアイコンが表示されなくて焦りました。 自分が入れたバージョンとドキュメントのバージョンをよく見比べましょう。(戒め)

今回私が入れたバージョンは0.19系でした。 つまりドキュメントはこちらです。

react-native-training.github.io

どうやらアイコンはこんな感じで埋め込めるそうです。

<Button
  large
  icon={{name: 'envira', type: 'font-awesome'}}
  title='LARGE WITH ICON TYPE' />

一方、間違って見てたのはこっち。 新しいから検索でこっちが先に引っかかりました。

react-native-training.github.io

今後はこんな書き方になるっぽいです。 タグで指定する感じ。

<Button
  icon={
    <Icon
      name='arrow-right'
      size={15}
      color='white'
    />
  }
  title='BUTTON WITH ICON COMPONENT'
/>

ついでに厄介だったのが、バージョンの違いでボタンの中で指定できるアイコンが違うことです。 0.19で sword-cross とかのアイコン使おうと思ったらエラーが出ました。

ドキュメントのバージョン確認大事。

ImageDataGenerator flow_from_directoryを使い、名前付きでクラスごとのデータを読み込む

kerasの ImageDataGenerator を使って画像を読み込み、kaggleの画像分類問題をやっていたのですが、 validationデータで良い正答率が出るにもかかわらず、testデータにするとうまく分類できない状況に陥りました。

原因は flow_from_directoryclasses を指定していなかったことでした。

現象

前提として、データは以下のようなディレクトリ構造で配置しています。

data/
    train/
        dogs/
            dog001.jpg
            dog002.jpg
            ...
        cats/
            cat001.jpg
            cat002.jpg
            ...
    validation/
        dogs/
            dog001.jpg
            dog002.jpg
            ...
        cats/
            cat001.jpg
            cat002.jpg
            ...

続いてコードです。

    train_generator = train_datagen.flow_from_directory(
        train_images, # 上記 train を指定
        target_size=(image_height, image_width),
        batch_size=batch_size,
        class_mode='categorical')

    validation_generator = test_datagen.flow_from_directory(
        test_images, # 上記 validation/ を指定
        target_size=(image_height, image_width),
        batch_size=1,
        class_mode='categorical')

flow_from_directoryclass_modecategorical にしておけば上記のディレクトリ構造からいい感じに分類をやってくれるそうです。 ということで、分類を test/validation/ 以下のディレクトリ名でやってくれると思ったのですが、そうではありませんでした。 このまま実行するとディレクトリ名に関係なく0からディレクトリ数までの数字を割り振って分類されます。 つまりdogs, catsではなく0, 1のように分類されるということです。

解決策

この問題を解決するためには classes を指定してやる必要があります。

    classes = ['dogs', 'cats']

    train_generator = train_datagen.flow_from_directory(
        train_images, # 上記 train を指定
        target_size=(image_height, image_width),
        batch_size=batch_size,
        classes=classes,
        class_mode='categorical')

    validation_generator = test_datagen.flow_from_directory(
        test_images, # 上記 validation/ を指定
        target_size=(image_height, image_width),
        batch_size=1,
        classes=classes,
        class_mode='categorical')

jsのcaseには中括弧をつけるべき

React Reduxで開発してて、Reducerに以下のようなコードを書いてたら case Bhoge で怒られました。

switch (type) {
  case A:
    const { hoge } = state;
    return {
      ...state,
      hoge: hoge + 1,
    };
  case B:
    const { hoge } = state;
    return {
      ...state,
      hoge: hoge + 2,
    };
  default:
    return state;
}

エラーメッセージは以下のように言ってます。 Duplicate declaration "hoge" (null)

どうやら hoge が二重定義されてるっぽいです。 以下のように括弧をつければ解決。 caseを括弧で閉じてやらないとスコープが制限されないんですね。。。

switch (type) {
  case A: {
    const { hoge } = state;
    return {
      ...state,
      hoge: hoge + 1,
    };
  }
  case B: {
    const { hoge } = state;
    return {
      ...state,
      hoge: hoge + 2,
    };
  }
  default: {
    return state;
  }
}