Photoruction工事中!

Photoructionの開発ブログです!

「工数見積」を受け取る側のマインドの話

はじめに

おはようございます。PMグループの神澤です。

今回は、プロジェクトを進める上で欠かせない「工数見積」の受け取り方について語ろうと思います。

今回のテーマ

工数見積は、簡単に言えば「どれくらいの期間でリリースできるか」を考えるための要素です。

プロダクトを販売する企業の人であれば、自社が作るプロダクトがいつ進化してユーザーに提供され始めるかは、誰しもが気になる点だと思います。

ただ、Amazonの配達時刻のような、想定外の事が無ければ予定通り行きます、といった期待感と異なり、多くの人は一度はリリース延期というイベントを経験していると思います。

ここでは、リリース日を決めるための内訳、工数見積に焦点を当てて、どのような要素が含まれるかを説明します。

工数見積に関係する人

プロダクトの開発の流れは、大きくは「要求→設計→開発→リリース」という形です。

この開発の流れに関わる人は、ビジネス、PM、エンジニアが主となり、デザイナー、QA、クライアント、といった関係者がいます。

一つのプロジェクトに対して、関係者が多くなるほど、「専門知識」や「把握情報量」に差が出ます。逆にいうと、開発の仕方を詳しく知らない人、運用を詳しく知らない人も発生します。(そのため関係者全員をチームとして考え、補いながら専門分野の役割分担をして開発を行います)

この「関係者が多くなるほど」という点が重要です。

限りなく少人数なチームであれば、コミュニケーションロスは起きづらく、どこまでどのように分からないのか、という温度感が理解しやすく、結果、工数見積もその内訳に対する期待感がズレにくいです。

逆に、関係者(開発チーム外だけでなく、開発チーム内メンバーも含む)が増えれば増えるほど、把握情報量を全員が100%持つ事が難しくなり、コミュニケーションによる伝達が難しくなります。

では、このコミュニケーションロスの内容にはどのような事例があるかを挙げていきます。

受け取り手の期待ズレ

  • 開発者は、全ての開発を事前に設計できるとは限らない(知識や経験の期待ズレ)
  • 開発者は、自分の技量を正確に把握しているとは限らない(=1日で出来るかという予測の難しさ)
  • 要件を決める人(PM等)は、要求を達成するための項目洗い出しを網羅できているとは限らない
  • 要求する人は、相手に全てを伝え切れているとは限らない

見積側の技量とは異なる問題

  • 要件が漏れているとは、予想する対象が存在していないということ(=見積項目抜け)
  • 要件が期待と異なっていると「訂正」と「再開発」という作業が生まれる
  • 要件が曖昧だと、実際の作業内容も曖昧となる。曖昧な箇所には、予想さえ出来なかった「やるべきだったタスク」が含まれる

じゃあどんなことに気を付ければいいんだろうね

工数見積」の捉え方には難しさがあり、その内容をコミュニケーションロスの具体例を使って一部書いてみました。

ではどうするか。全ての要件を完璧にすれば問題ない、と言ってしまえば、それも選択肢としてありますが、現実は非常に難しいです。

見積内容の認識齟齬以外にも、テキストの用語が相手に伝わるか、背景理解に差があるか、十分に内容把握する時間的人的余裕があるか、等、懸念すべき事は多くあり、辞書のような仕様書にも、街宣広告のような洗練されたテキストにも、万能な価値はありません。

私が思う重要な点は、関係者同士の「相互理解」です。

ビジネス側にも開発側にも、「専門知識」があり「把握情報量」があります。関係者を各専門家として対等に問題に向き合い、チームとしての調和が必要だと考えます。

業務上のメンバーは、言ってしまえば寄せ集めのメンバーとなる事が多いです。スポーツでいうと近所の人を集めた草野球や、偶然同じ学校にいた部活動のような状態です。プロチームのようにドラフトで人を集める手法もありますが、どの企業でもそれが可能とは限りません。

このような状態で考えるべきは、「このチームで出来る最善の策は何か」を「考え続ける」ことだと思います。

上記の箇条書きは、「あの人なら出来ると思ってた」「自分なら出来ると思ってた」というマインドだと特に起こりやすいと思っています。実際には誰しも不完全な点はあり、得意だから全能ということはなく、だからこそ集団で物事に当たることに、価値があると思っています。

工数見積」(だけに限る話ではありませんが)でも、工数を計算する上での内訳内容や、どのような人が考えたか、そのシチュエーションや環境はどうだったか、を全員が考慮し、今いる人が出来る最大限のパフォーマンスを出そうというマインドを持っていられると、自然と把握情報量の差異が減り、徐々に信頼できる工数の、算出が可能、もしくは受け取り方が可能となるかなと思います。

あとがき

私はブログのような形で長文を書くことが苦手なので、お見苦しい点もあるかと思います。今後の課題です。

ちなみに、フォトラクションでは「あの人がやってくれるから」という他者頼りなマインドを持つ人がおらず、常に課題を見つけ解決していきたいと考える人が多いため、上記のような問題に直接ぶつかった事はないです。可能性の話や懸念の検討の際に出たものをメモしていた内容となります。

今後もフォトラクションのチームやメンバーが進化していくことで、更に細かく本質的な課題にぶつかっていく機会があると思うので、その際にはまたこのような形で共有が出来ればと思います。

 

株式会社フォトラクションでは一緒に働く仲間を募集しています

新規プロダクト開発におけるPMの取組み

フォトラクションでプロダクトマネージャー(PM)をしています千葉です。

3月から新規プロダクトの担当になりました。

初めて携わるプロダクトのためサービス内容や社内の運用体制などわからないことばかり・・・。

そんな状況から開発する機能を具体化するまでの取組みについて振り返ってみました。

目次


すぐに取り組んだこと


まずはプロジェクトの現状把握からです。次のことがわかりました。

  • 顧客の課題・サービスの問題点は明らかになっている。
  • 事業戦略には「○○月には使える状態にする」という開発の大まかな目標は描かれている。
  • 画面イメージや要件定義書など開発するものを具体的に示すものはまだない。

なのでプロダクトを通して顧客をどのような姿にしたいのかと、そしてそれを実現するために開発するものを決めることから着手しました。

取組み①ターゲットユーザーとフォトラクションの強みを整理


私が担当するプロダクトは初期フェーズなので、狙いたいユーザー層に価値をしっかり提供することが大事だと考えました。そこで、事業戦略・ビジネス戦略をよく理解し、プロダクトの位置づけを明確にしました。それからターゲットユーザーやフォトラクションの強みを整理していきました。

取組み②顧客の声を聴く・ニーズを理解する


続いて顧客ニーズを明らかにするために顧客の声を聞くことにしました。フォトラクションは多くの建設会社と定例を実施しており、ユーザーから率直な意見を聞く機会に恵まれています。

ヒアリングを通して自分の想像していた業務以外にも手間がかかっている現状を知ることができました。顧客の言葉を紐解きながら潜在化する顧客ニーズが少しずつ見えてきた気がします。

やはり誰のフィルターもかかっていない1次情報を見ることの大切さを改めて感じました。

取組み③顧客ニーズから顧客の理想像を具体化


顧客のニーズの実現には、多角的な目線でプロダクトを設計することが大切だと考えています。

そのため、顧客ニーズを整理した後、顧客をどのような状態にしたいのか、どのようなユーザー体験(UX)が良いのかUIデザイナーと時間をかけて議論しました。

初期段階の議論は、具体的なUIなどはまだないので、空中戦になりがちでした。

そこで具体的なアイディアを持ち寄ることで認識を合わせ、少しずつ目線を合わせていきました。

参考までに、今回はアイディア出しでmiroを活用しました。業務フローや考え方の整理・可視化に使えるので描画ツールはとても有効でした!

f:id:photoruction_tech_blog:20220404102543p:plain


これから

現在は理想のUXを機能要件に落とし込み、機能の設計を行っています。WebエンジニアやQAエンジニアもうすぐ合流していよいよプロジェクトが本格的に稼働します。

今後チーム開発・スクラムの導入により、ノウハウの蓄積やニーズへの柔軟かつ即応性が高い開発体制も構築していけるようになるでしょう。

まだまだ一歩目を踏み出したばかりのプロジェクトですが、ニーズを叶えるプロダクトを実現できるように一歩一歩前進していければと思っています。

建設業は2024年労働時間の上限規制を控え、生産性向上が急務となっています。テクノロジーで産業を変えていきたい方、ニーズを模索しながら実行してくプロダクト作りに共感や面白みを感じてくれる方、ぜひお待ちしております!

株式会社フォトラクションでは一緒に働く仲間を募集しています

シェルスクリプトを使用してEC2を操作する

はじめに

今年も赤や白の梅の花が咲き乱れ、花粉が舞う季節がやってまいりました。

あいにく、僕はちょっと目が痒いくらいで済んでます(症状がひどい方、すみません)。

まったく関係ありませんが、3回目のワクチンの副反応もちょっと体がだるいなくらいで済みました。(良いのか悪いのかわかりませんが)

こんにちは、AIエンジニアの山下です。

さて、今回はシェルスクリプト初心者の僕がシェルスクリプトを使用しAmazonEC2を操作することを目的とした記事になります。

普段の業務でシェルスクリプトを書くことはほとんどなく、なぜシェルスクリプトを書こうと思ったかというとAmazonEC2を扱うことがあり、毎回AWSのコンソールから操作するのは面倒だし、AWS CLIコマンドラインインターフェイス)で行うにしても毎度毎度調べるのもどうかと思い、シェルスクリプトにまとめたら使いやすいのではと思いまとめてみました。

今回初めて0から書くうえで普段使うVScodeでなくVim使ってみたり、Linux screenを使ってみるなど初めてなことが多く刺激的でした(ただ、シェルスクリプトは業務で使うことは少なそう、、、)。余談ですが、Vimはやりがいがありそうで今後取り入れてみてVScodeと比較してみたいです。

*OSはMac

おさらい

シェルスクリプトとは

AWS CLIコマンドラインインターフェイス)とは

シェルスクリプト実行例

$ ./launch_ec2_instance.sh status
stopped

前提

以下、作成物→簡単な内容説明となります

作成物:その1

*前提としては EC2上にはインスタンスができている状態です

#!/bin/zsh

# set aws profile
PROFILE=default

# Get instance name: yamashta-devのinstance ids
INFOS=(InstanceId ImageId Keyname InstanceType \\
PublicIpAddress PublicDnsName State.Name)

# Define Function
function get_instance_info() {
    aws ec2 describe-instances \\
    --filters "Name=tag:name,Values=yamashita" \\
    --profile $PROFILE \\
    --query 'Reservations[*].Instances[*].'[$1]'' | jq -r '.[][][]'
}

IDS=`get_instance_info InstanceId`
# echo IDS: $IDS
DNS=`get_instance_info PublicDnsName`

# インスタンスの各情報を習得
if [ "$1" == "infos" ]
then
    select info in ${INFOS[@]}
    do
        echo `get_instance_info $info`
        break;
    done

elif [ "$1" == "status" ]
then
    echo `get_instance_info State.Name`

elif [ "$1" == "start" ]
then
    aws ec2 start-instances --instance-ids $IDS --profile $PROFILE

elif [ "$1" == "stop" ]
then
    aws ec2 stop-instances --instance-ids $IDS --profile $PROFILE

elif [ "$1" == "terminate" ]
then
    aws ec2 terminate-instances --instance-ids $IDS --profile $PROFILE

elif [ "$1" == "connect" ]
then
    ssh -i "~/.ssh/yamashita-rsa.pem" ec2-user@$DNS

elif [ "$1" == "file_transfer" ]
then
    # ローカルの任意のファイル($2: file path)をインスタンスに転送 -> :/home/ec2-user/
    scp -i ~/.ssh/yamashita-rsa.pem $2 ec2-user@$DNS:$3

elif [ "$1" == "reboot" ]
then
    aws ec2 reboot-instances --instance-ids $IDS --profile $PROFILE

elif [ "$1" == "transfer_file_locally" ]
then
    # インスタンスから任意のファイル($2: file path)をローカル($3)に転送
    # $2: file path, $3: local path
    scp -i ~/.ssh/yamashita-rsa.pem ec2-user@$DNS:$2 $3

else
    echo "Usage: $0\\ninfos | status | start | stop | terminate | connect | \\
file_transfer | reboot | transfer_file_locally"
fi
  • 上から簡単に内容を説明していきます。
#!/bin/zsh
  • これはスクリプトの一行目に記述する定型文で、この一行目で、実行時どのシェルでスクリプトを実行するかが決まります(bashなら #!/bin/bashとか)
# set aws profile
PROFILE=default
# Get instance name: yamashta-devのinstance ids
INFOS=(InstanceId ImageId Keyname InstanceType \\
PublicIpAddress PublicDnsName State.Name)
# Define Function
function get_instance_info() {
    aws ec2 describe-instances \\
    --filters "Name=tag:name,Values=yamashita" \\
    --profile $PROFILE \\
    --query 'Reservations[*].Instances[*].'[$1]'' | jq -r '.[][][]'
}
IDS=`get_instance_info InstanceId`
# echo IDS: $IDS
DNS=`get_instance_info PublicDnsName`
# インスタンスの各情報を取得
if [ "$1" == "infos" ]
then
    select info in ${INFOS[@]}
    do
        echo `get_instance_info $info`
        break;
    done
elif [ "$1" == "status" ]
then
    echo `get_instance_info State.Name`

elif [ "$1" == "start" ]
then
    aws ec2 start-instances --instance-ids $IDS --profile $PROFILE

elif [ "$1" == "stop" ]
then
    aws ec2 stop-instances --instance-ids $IDS --profile $PROFILE

elif [ "$1" == "terminate" ]
then
    aws ec2 terminate-instances --instance-ids $IDS --profile $PROFILE
elif [ "$1" == "connect" ]
then
    ssh -i "~/.ssh/yamashita-rsa.pem" ec2-user@$DNS
  • 引数に 「connect」 を指定するとローカルのターミナルから起動しているインスタンスSSH接続する。(DNSに関してはこちら
elif [ "$1" == "file_transfer" ]
then
    # ローカルの任意のファイル($2: file path)をインスタンスに転送 -> :/home/ec2-user/
    scp -i ~/.ssh/yamashita-rsa.pem $2 ec2-user@$DNS:$3
  • 第一引数に 「file_transfer」 を指定する場合はローカル上にある任意のファイルをインスタンス上の指定したパスに転送する。第2、第3引数にパスの指定が必須。
    • 第2引数:ローカルのファイルパスを指定
    • 第3引数:インスタンス上のパス(ファイルを置きたい場所)
elif [ "$1" == "reboot" ]
then
    aws ec2 reboot-instances --instance-ids $IDS --profile $PROFILE

		elif [ "$1" == "transfer_file_locally" ]
then
    # インスタンスから任意のファイル($2: file path)をローカル($3)に転送
    # $2: file path, $3: local path
    scp -i ~/.ssh/yamashita-rsa.pem ec2-user@$DNS:$2 $3
  • 引数に「transfer_file_locally」を指定するとインスタンスの任意のファイルをローカルの任意の場所に転送します。第2、第3引数にパスの指定が必要になります。
    • 第2引数:インスタンス上の任意のファイルパス
    • 第3引数:ローカル上の任意のパス(ファイルを置きたい場所)
else
    echo "Usage: $0\\ninfos | status | start | stop | terminate | connect | \\
file_transfer | reboot | transfer_file_locally"
  • 最後に第1引数を指定せずに実行すると指定可能な引数を教えてくれます

    f:id:photoruction_tech_blog:20220315140419p:plain

作成物:その2

長くなるので詳細な説明は省くうえに、まだ作成途中ですが、以下ではローカルからCLIを利用し、AWS ECRでrepsitoryの作成、または、ECRにログインできるようにしてます。(最終的にはローカルでビルドしたDockerイメージをECRにプッシュ、EC2インスタンスにpullし、コンテナを起動できるようなシェルスクリプトにする予定です。)

  • readコマンドでAWS CLIのプロファイル名を入力
  • ECRのログインIDを取得
  • ECRにログイン、または、repositoryを作成
#!/bin/bash

read -p "input profile name! " str

aws_account_id=`aws sts get-caller-identity --query 'Account' --output text --profile="$str"`
echo aws_account_id: $aws_account_id

select var in login create_repo Usage

do
    if [ "$var" == "login" ];then
    	aws ecr get-login-password --region ap-northeast-1 --profile "$str" | docker login --username AWS --password-stdin "$aws_account_id".dkr.ecr.ap-northeast-1.amazonaws.com
	
	elif [ "$var" == "create_repo" ];then
		read -p "input repository name! " r_name
    	# $r_nameにrepository-nameが必要
	    aws ecr create-repository \\
	    --repository-name "$r_name" \\
	    --image-scanning-configuration scanOnPush=true \\
	    --region ap-northeast-1 \\
	    --profile "$str" 

	elif [ "$var" == "Usage" ];then
        echo "Usage: login | create_repo"
    fi
    break
done

感想

  • 記事を書きながら思ったんですが、作成物:その1に関してはif文よりもselectコマンドとreadコマンドを使った方が使い勝手が良かったのではと思いました。
  • 最終的にはローカルでビルドした Dockerイメージを ECRにpush → ECRからイメージをEC2にpull → EC2上にコンテナを起動し、VSCodeからSSH接続するところまでやってみているので、また機会があればその辺りのことを加筆していきたいのと、冒頭でも触れましたが、Vim環境も構築しがいがありそうなので機会があれば記事にしたいと思います。

株式会社フォトラクションでは一緒に働く仲間を募集しています

パッケージの管理ルールを制定してみた(第2弾)

はじめに

こんにちは!株式会社PhotoructionでAIエンジニアとしてインターンをしている渡邉圭太郎です。今回は、昨年末のアドベントカレンダーでの内容の続きで、CIルールについて執筆したいと思います。

アドベントカレンダーにおいて紹介しましたが、僕の所属するAIチームではよりAI開発やAI精度向上に力を入れるための改善点としてCI/CDの必要性が上がっていました。そこで、AI解析をパッケージ化やAPI化する際のCIルールを書きたいと思います。

構成図

今回制定したCIの構成図は次の図の通りです。また、Docker等のプロジェクト環境構築についてはアドベントカレンダーでの記事を参照してください。

f:id:photoruction_tech_blog:20220302182804p:plain

 

手順

  1. まず、cloudformationを用いてAWSのS3にリソースを作成し、テストデータを保存しておきます。
  2. 続いて、Github Actionsにおいて実行するためworkfileを作成します。workfileはプロジェクト配下の.github/workfilesディレクトリに配置します。
    ├── .github
    │   └── workflows
    │       └── **test.yml**
    ├── docker-compose.yml
    ├── Dockerfile
    ├── README.md
    ├── tests
    └── src
  3. 実行タイミングと環境変数を定義します。

    name: detection
    
    on: [ push, pull_request ]
    # ここでgithub actionsを走らせるタイミングを決められる。
    
    env:
    # 必要な環境変数を定義する
      AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
      CLI_AWS_ACCESS_KEY_ID: ${{ secrets.CLI_AWS_ACCESS_KEY_ID }}
      CLI_AWS_SECRET_ACCESS_KEY: ${{ secrets.CLI_AWS_SECRET_ACCESS_KEY }}
    
      ACCOUNT_ID: ${{ secrets.RD_ACCOUNT_ID }}
      AWS_ROLE_ARN: arn:aws:iam::${{ secrets.RD_ACCOUNT_ID }}:role/cli-role
    
      BUCKET_NAME: detection
    
      PIP_CACHE_DIR_DETECTION: /tmp/cache/pip_detection
    
  4. パッケージ内関数テスト用のjobを記述します。

    jobs:
      function-test:
        name: パッケージ関数テスト
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v2
    
          - name: Configure AWS credentials
            uses: aws-actions/configure-aws-credentials@v1
            with:
              aws-access-key-id: ${{ env.CLI_AWS_ACCESS_KEY_ID }}
              aws-secret-access-key: ${{ env.CLI_AWS_SECRET_ACCESS_KEY }}
              aws-region: ${{ env.AWS_DEFAULT_REGION }}
              role-to-assume: ${{ env.AWS_ROLE_ARN }}
              role-duration-seconds: 1200
              role-skip-session-tagging: true
    
          - name: Download ocr key file from S3
    				# 解析テストに必要なファイルをS3からダウンロード
            run: |
              aws s3 sync s3://$BUCKET_NAME/ocr_key ./ocr_key --quiet
    
          - name: Cache detection pip
            uses: actions/cache@v2
            with:
              path: ${{ env.PIP_CACHE_DIR_DETECTION }}
              key: ${{ runner.os }}-detection-python-cache-${{ hashFiles('./pyproject.toml') }}
              restore-keys: |
                ${{ runner.os }}-detection-python-cache-
    
          - name: Build the docker-compose
            run: docker-compose up -d detection
    
          - name: detection install modules
            run: |
              docker exec detection /bin/bash -c "poetry install"
              docker exec detection /bin/bash -c "chmod -R 755 /root/.cache/"
    
          - name: detection test
    				# パッケージ内関数テストの実行
            run: docker exec detection poetry run pytest tests -s
    
  5. AI解析において多様なデータによるパッケージの評価検証を行うために、jobを追記します。ここでは、実施するテストケースごとにjobを作成しています。また、これらのテスト結果には閾値を設け閾値以上のスコアが得られない場合は失敗という結果と共にCI(/CD)がストップします。

    jobs:
    	function-test:
    		...
    	caseA_test:
    		...
    	caseB_test:
    		...
    

    テストケースごとのjobの例

    caseA_test:
        needs: function-caseA
    		# パッケージ内関数テストが成功しないと実行しない仕様にする
        name: caseA_test
        runs-on: ubuntu-latest
        env:
          TEST_DATA_DIR: caseA_test
        steps:
          - uses: actions/checkout@v2
    
          - name: Configure AWS credentials
            uses: aws-actions/configure-aws-credentials@v1
            with:
              aws-access-key-id: ${{ env.CLI_AWS_ACCESS_KEY_ID }}
              aws-secret-access-key: ${{ env.CLI_AWS_SECRET_ACCESS_KEY }}
              aws-region: ${{ env.AWS_DEFAULT_REGION }}
              role-to-assume: ${{ env.AWS_ROLE_ARN }}
              role-duration-seconds: 1200
              role-skip-session-tagging: true
    
          - name: Download ocr key file from S3
    				# 解析に必要なファイル(ocr_keyファイルなど)をS3からダウンロード
            run: |
              aws s3 sync s3://$BUCKET_NAME/ocr_key ./ocr_key --quiet
    
          - name: Download test files from S3
    				# テストデータをS3からダウンロード
            run: |
              aws s3 sync s3://$BUCKET_NAME/test_sample/$TEST_DATA_DIR ./tests/dataset --quiet
    
          - name: Cache detection pip
            uses: actions/cache@v2
            with:
              path: ${{ env.PIP_CACHE_DIR_DETECTION }}
              key: ${{ runner.os }}-detection-python-cache-${{ hashFiles('./pyproject.toml') }}
              restore-keys: |
                ${{ runner.os }}-detection-python-cache-
    
          - name: Build the docker-compose
            run: docker-compose up -d detection
    
          - name: detection install modules
            run: |
              docker exec detection /bin/bash -c "poetry install"
              docker exec detection /bin/bash -c "chmod -R 755 /root/.cache/"
    
          - name: verification test
    				# 評価検証テストの実行
            run: docker exec detection poetry run python tests/verification.py -s
    
          - name: Upload result file from S3
    				# 評価検証結果をS3にアップロード
            run: |
              aws s3 cp ./tests/dataset/result s3://$BUCKET_NAME/test_sample/$TEST_DATA_DIR/result/$(TZ="Asia/Tokyo" date "+%Y-%m-%d_%H-%M-%S") --quiet
    

    おわりに

    今回、チーム内においてCI(/CD)ルールを制定したことにより開発のスピードがかなり上がったと感じました。また、改修の際のテストが以前に比べかなり早く終わり次の開発へ素早く移行することも可能となりました。

    個人的には、このような技術はさらに進化し出現するため最先端を走っていけるようなエンジニアになりたいです。ここまで読んでいただきありがとうございました。

    株式会社フォトラクションでは一緒に働く仲間を募集しています

iOSチームの運用ルールの変遷

はじめに

PhotoructionでiOS テックリードをしている數田です。

今回は私が加入してからのiOSチームの運用ルールの変遷の一部を振り返ります。

チームで開発するの運用ルールが定まっていなかったので、チームを運営する際の参考になる箇所もあるかと思います。

運用ルールで手動で行うことを増やすと漏れが発生するので、基本的に自動化できることしか取り入れていません。

運用ルールの変遷

ビルドが失敗する

一般的にはビルドが通る状態でPull Request(以下 PR)が作成されますが、ブランチをチェックアウトして手元でビルドすると失敗することが、多々ありました。

PRをレビューする都度、手元でビルドの確認するのはプログラマーの三大美徳に反します。

Bitriseを導入しPRのチェックが通っているものレビューしてマージする運用に変更しました。

マージタイミングがわからない

複数人で開発しているとPull Requestが多数作成され、すぐにレビューしてマージしたい衝動に駆られます。リリースタイミングが異なるPRも出てきて、間違ってマージしてしまう問題が発生してしまいます。

PRにリリース時期の記載がなかったため、確認に時間を取られていました。リリース時期を

PRのタイトルや説明に記載する方法が最初に思いつきますが、以下のような記述の揺らぎが発生します。

  • 2022年1月リリース
  • 2022年1月31日リリース
  • バージョン x.y.z

記述の揺らぎが発生せずGithubマイルストーンを設定することにしました。マイルストーンで絞り込めるようになり、優先的にレビュー、マージするPRがわかりやすくなります。

手動でマイルストーンを設定する運用を行っていると設定漏れが起きます。Dangerでマイルストーンが設定されているかのチェックを追加します。

PRの種類による絞り込み

PRのレビューする際に機能改修やバグフィックス、ライブラリのアップデートなど変更の種類によってレビューの観点が異なります。

開発用ライブラリのアップデートのPRなどすぐにマージしても問題ないものを一覧からタイトルのみで探さないといけないので多少手間がかかります。

PRの種類で絞り込めると手間が減るのでPRの種類の情報を付与する方法を検討した結果、ブランチ名で判別できそうなので、プレフィックスによってラベルをつけるGithub Action PR Labelerを追加しました。

現在iOSのプロジェクトでは以下の種類のラベルをつけて運用しています。

Feature: ['feature/*']
refactor: ['refactor/*']
Issue: ['issue/*']
bug: ['fix/*', 'bugs/*']
cre: ['bugs/*']

バグ解消

バグ解消のためCREチームがあり、PRはQAチームでテスト完了後にマージする運用となっています。CREが担当したPRは cre のラベルが付与されているの絞り込みできますが、テストが完了する前にマージしてしまうと、不具合が発生する可能性があるので、マージ前にテストの状況を把握する必要が出てきます。

PRにテストの状況を付与する方法を検討して、Github projectsでテストの状況をトレースすることで解決しました。

今後

基本的には自動化できる運用ルールを追加、見直しして開発で楽できるようにチーム運営を行っていきます。

常に改善し続けていくiOSチームと一緒に働く方お待ちしてます。

 

株式会社フォトラクションでは一緒に働く仲間を募集しています

入社したらAndroid環境が良くなかったので良くした件について

はじめに

はじめまして。モバイルチームリーダーのAndroidエンジニアの齊藤です。

2019年10月にPhotoructionに入社し、現在で2年4ヶ月経ちました。

元々はSES会社に勤めていて、様々な企業に常駐でAndroidを中心にWebやサーバサイド等の開発をしていました。

Photoructionに入社した時点ではAndroidエンジニアが自分一人でした。

当初Android版Photoructionの開発は開発未経験の方が行い、その後業務委託やオフショアの方が機能開発・不具合修正・リリースを担当していました。そのため、開発基盤に特にルールが存在しない状態で、入社後に行ったのは開発環境や運用の変更でした。

今回はその時のことを振り返っていきたいと思います。

git-flowの導入

入社前の運用と問題点

Photoructionではバージョン管理にGitを使用していましたが、Androidチームでは特にgitの運用方法が決まっていたわけではありませんでした。

master, devブランチの他、エンジニアの名前ブランチフォルダだったり、バージョン別のブランチだったりと自由な運用がされていたため、どのブランチがどの内容なのか判断しづらい状況でした。

改善内容

そのため以下のようなgit-flowで管理することで、それぞれのブランチの役目を決めました。

master : プロジェクトの親ブランチで、リリース済みの最新ソースとなる。

develop : 開発用ブランチ。プロテクト対象で直Pushはできない。featureブランチのbaseとなり、featureの機能をマージすることで更新する。

feature : 機能開発用ブランチ。developからの作成が中心で、developに対して、機能を追加していくためのブランチ。「feature/xxxx」という形で、xxxxには機能に対する意味あるIDで管理する。

release : リリース用ブランチ。通常のリリースフローのためのブランチで、リリース可能な段階になったdevelopから作成される。「release/1.2.3」とリリースするバージョン名で管理する。

hotfix : 緊急不具合対応ブランチ。問題のあるリリース中のソースが存在するmasterブランチから作成される。「hotfix/1.2.4」とリリースするバージョン名で管理する。

社内だけではなく、業務委託の方やオフショアに対しても説明を行い、運用の徹底をしました。

featureブランチ名は、当時Trelloでタスク管理をしていたので、feature/dev-{カードID}で運用をするようにしました。

f:id:photoruction_tech_blog:20220202144602p:plain

 

※現在はタスク管理をBacklogやgithub issueで行っているためそれぞれのissueIDをブランチ名として利用しています。

deploygate対応

入社前の運用と問題点

元々社内の開発中及びリリース対象アプリの社内配布(テスト用)は、GoogleDriveにアップロードし、Android端末でダウンロードを行い、APKファイルからインストールする方法を取っていたため、端末によってインストールまでの操作が異なっているなど、手間が掛かり社内配信のハードルを感じました。

また、productFlavorsは接続先環境を切り替えるためにproduction, dev, alphaの3件存在しましたが、applicationIdSuffixが特に設定されていなかったため、一つの端末にインストールできるアプリが一つのみで、そのアプリがリリース用なのか開発中のものなのかを判断することが難しい状態でした。

f:id:photoruction_tech_blog:20220202150320p:plain

 

改善内容

deploygateへの移行をしました。配布も簡単で、アプリさえ入れておけば社内端末で好きなバージョンのアプリを入れることができるためです。

テスターさんとのやりとりも、「dev#123で確認お願いします」と伝え易くなり、テスターさん以外の社内のメンバーにも気軽に開発中のアプリを触ってもらえるようになったと思います。

また、applicationIdSuffixが特に設定されていなかったのでdevとalphaに対して設定しました。

f:id:photoruction_tech_blog:20220202145700p:plain

 

applicationIdSuffixを設定したことにより、それぞれが別のpackageNameになり、deploygateで同じアプリを環境違いでアップすることができ、一つの端末に環境別のアプリをインストールすることができるようになりました。

また、端末に環境別アプリがインストールできるようになったことで、どのアプリがリリース用・開発用かをわかりやすくするため、アプリのアイコンの色を変更しました。

f:id:photoruction_tech_blog:20220202145841p:plain

 

現在は会社やプロダクトのロゴが変更になったことにより、新アイコンになっています。

f:id:photoruction_tech_blog:20220202145901p:plain



アプリケーションのビルド

入社前の運用と問題点

アプリのビルドは、Android開発者がいなかったためオフショア側でビルドしたものを受け取り、それを配信する形を取っていたようです。社内にAndroidエンジニアがいなかったため、社内でビルドができていませんでした。

改善内容

配信方法がdeploygateになったこともあり、社内でアプリビルドが簡単にできるようにshellを作りました。

現在では、fastlaneで現在の開発ブランチのアプリを作成しdeploygateへアップロードしたり、github actionsで特定のPRコメントによってアプリをビルドしてdeploygateへアップロードする方法も運用しています。PRコメントでビルドする方法は、エンジニアだけでなく開発環境のないQAエンジニアも任意のタイミングでビルドしてアップすることができるので非常に便利になりました。

公開範囲の拡大と多言語対応

Photoructionは海外の建設現場での使用を考慮することが必要で、特にAndroid端末はiOSよりも海外でのシェアが高いので、重要なアプリとなります。

入社前の運用と問題点

公開範囲設定が2-3カ国しか設定されておらず、実際に利用される予定の地域が入っていなかったりと適切ではありませんでした。

また、海外での利用を考慮すると多言語対応も必要になりますが、アプリは日本語文言が全てstrings.xmlで管理されておらず、ソースコード内にハードコーディングされているものも多々ありました。

改善内容

公開範囲の拡大を行い、ハードコーディングされた日本語文言のstrings.xmlへの移行と多言語対応用のリストアップも行いました。

現在対応している言語は、日本語・英語・インドネシア語ベトナム語・中国語(繁体・簡体)です。

まとめ

入社直後は、Android版の開発環境がとても良いとは言えない状態だったため、最低限の形に直していきました。

Photoruction入社前に担当していた大手企業のアプリでも同じような状態だったこともあったので、Android版のリポジトリを見た時は特にガッカリしたとかはなく、そんなものだよね。という感じでした。

その後、メンバーが増えたり、豊富な知識を持った業務委託の方のご協力のもと徐々にAndroidの開発環境も良くなってきています。

他にも初期の頃にまともに動作しない不具合もありましたが、その話は次回以降お話したいと思います。今回はこのような記事でしたが、今後はもう少し技術寄りの記事にしたいです。

最後に

2021年のアドベントカレンダーAndroidチームの現在、そして今後のアーキテクチャについての記事がありますので、よろしければご覧ください。

 

株式会社フォトラクションでは一緒に働く仲間を募集しています

ゼロイチからのプロダクト開発変遷

こんにちは!Photoructionで開発責任者をしています黒田です。

私がPhotoructionに関わり始めたのは起業して3ヶ月目の2016年6月で、かれこれ6年目を迎えました。

初期から関わらせてもらってきた中で、これまでの開発の体制や進め方などを中心にトピックスを交えながら立ち上げ〜現在に至るまでを時系列に振り返っていこうと思います。

目次


 

黎明期(2016年6月〜2017年)


プロダクトはもちろん会社の認知はほぼ無い状態。とにかく早く市場に進出する必要があった。

立ち上げ

  • 起業する前から代表の中島が1人で作っていたプロダクトをブラッシュアップしていく形で始まる。(実際はフレームワークから入れ直し)
  • プロダクトのプラットフォームはWEB、iOS
  • 開発は全員(当時3人。代表の中島、iOSエンジニア、私)で作るべき機能を決めて常にコミュニケーション取りながら朝から晩まで開発に明け暮れてた。
  • 各々役割はあるが市場調査からインフラ、デザイン、開発などなんでもやるスタンス。

β版リリース(2016年11月)

  • ローンチ前にして実際に現場で使っていただけるというお声をいただき、最小限の機能を用意してβ版をリリースする。

(自分たちが作ったプロダクトが初めて建設業の中に交わる実感が持てたことにすごく感動した記憶があります)

正式ローンチ(2017年7月)

  • プロダクトとして最低限業務で使える機能を用意して正式ローンチを行う。
  • このころになるとエンジニア人数も5人ほどになり、またプロダクトのプラットフォームもWEB、iOSに加えAndroidの開発も始まる。
  • 開発サイクルをどんどん回す必要があったためスクラム開発を導入する。
  • 1週間1スプリント、週1リリースのサイクル開発を進める。

ローンチ後

  • ビジネスサイドも立ち上がり、社員数も10人前後となる。

  • 少しずつ市場に認知を得始めるが、他社サービスと比べるとまだまだ劣ってる状態だったため同じ土俵に立つためにもひたすら機能追加を行う。

  • このころの開発チームとしてはプラットフォーム(WEB、モバイル)に関係なくワンチームでプロダクトのブラッシュアップに努める。

    f:id:photoruction_tech_blog:20220114145304p:plain

    当時は開発チームとしてベロシティを計りながらスプリントを回していた。

    • スプリントレビューは社員全員で行う。

拡大期(2018年〜2019年)


2018年

  • ユーザー数も徐々に増えていき、これに伴い要望数や不具合報告も増えいく。
  • エンジニアも総出で展示会に出店する(エンドユーザーの業務を直に聞いてキャッチアップし、それを踏まえて自分の言葉を通してプロダクトをイメージしてもらうことはとても良い経験でした)

  • AI開発の立ち上げ。

  • プロダクトとの領域を広げるために協業サービスとPhotoructionを連携させる開発する。

  • 全社導入にあたり、お客様の基幹システムとPhotoructionを連携さるためのAPIを開発し公開する。

  • プラットフォーム別(WEB、iOSAndroid)で開発チームを分ける。

    f:id:photoruction_tech_blog:20220114145925p:plain

  • スプリントレビューは全エンジニア+CS参加で行う。

2019年

  • 海外現場への導入などもあり多言語対応を行う。
  • 開発手法・体制の変更
    • エンジニア数も増え(10人前後)、スクラム開発のスプリント計画・レビューが形骸化していく。

    • ユーザーを獲得するための開発も増えていく中で、リリース計画を精緻にするためスクラム開発からウォーターフォール型へとシフトしていく。

      f:id:photoruction_tech_blog:20220114150201p:plain

    • 体制も開発プロジェクトを起点にした各職能に合わせたメンバーをアサインする手法を採用する。

      f:id:photoruction_tech_blog:20220114150516p:plain

       

成長期(2020年〜2021年)


2020年

  • PMグループ、デザイングループ、QAグループの立ち上げ。

    f:id:photoruction_tech_blog:20220117133956p:plain

  • 出社しての開発からリモート中心の開発へと切り替わる。

2021年

  • 既存の価値を守る部隊(トラブル、CSの技術サポート等)としてCREグループを立ち上げる。

    f:id:photoruction_tech_blog:20220114150652p:plain

  • より柔軟なDXによる価値提供を行うために、個社毎に用意したMicroServiceを利用して連携できる仕組みを構築する。

    f:id:photoruction_tech_blog:20220114150749p:plain

  • 機能のリニューアルなど大規模な案件が複数走る。

これから


開発プロジェクトを起点にしたウォーターフォールでの開発を行なってきた中で様々な課題も出てきました。

  • プロジェクト発生ごとにリソースの調整が必要
  • プロジェクトごとにチームが組成され開発が終了すると解散するため、チームワークが築けなかったり、機能数の増加や複雑性に伴いノウハウの蓄積がしづらくコミュニケーションコストが肥大化
  • チーム開発を行う上で意志を持った開発がしづらい などなど

これからの新たな開発体制に向けて

現在そういった課題を解決するためにも、体制や開発フローを見直して見直していっています。

直近では開発チーム(メンバー)を固定化し、領域内での開発により注力できるようなアジャイルチームにシフトしていきます!

  • ドメイン(注力領域)ごとのチームと、職能ごとのグループのマトリクス構造で構成。
  • ドメインごとのチームは、エンジニア・デザイナーなどの職能を横断したメンバーで構成され、ドメインごとの意志決定の促進を重視。
  • 職能ごとのグループではエンジニア横断でのノウハウの蓄積や横断課題の対処するための活動を実施し、相互成長や品質向上を重視。

f:id:photoruction_tech_blog:20220114151001p:plain

まとめ


立ち上げからの6年を思い返してみると(かなり端折ってますが(笑))色んなことがありましたが、現在はカオスな部分を最適化・仕組み化していくフェーズだと考えています。

まだまだ多くの組織課題や技術課題、プロダクトとしての課題も盛り沢山な状況ではありますが、プロダクトをはじめ全てはより良くしていくために取り組んでいます。

一つづつ紐解いて模索しながらもどうすべきかを実行してくことに共感や面白みを感じてくれる方、これからのPhotoructionの歴史を一緒に造り上げていってくれる方、ぜひお待ちしております!

最後まで読んでいただきありがとうございました。