開発合宿@箱根湯本

f:id:jxpress:20191008062416j:plain

はじめに

こんにちは!CTOの柳です。今回のブログでは、9月某日に行われたJX通信社恒例の開発合宿について紹介したいと思います。

これまで、JX通信社では年1回のペースで開発合宿を実施してきました。

tech.jxpress.net

www.wantedly.com

合宿のテーマ

毎年、弊社の開発合宿では、さまざまなテーマに取り組んできました。社内環境・ツールの整備、CI/CDの導入、去年はビジネスサイドのメンバーを巻き込んだデータ分析講習会や普段できない業務改善について取り組みました。

今年はCTOである私が幹事を引き受けたので、職権乱用メンバーを説得し、より尖ったテーマに取り組みました。ずばり「rebuild」です!

組織とプロダクトが大きくなっていく中、普段の業務では慣れた技術スタックばかり使うようになってしまいます。世の中に面白い技術が続々と登場しても、中々触れる機会がありません。

そこで、合宿では「新しい技術スタックで、既存プロダクト1つを0から作り直すこと」(= rebuild)にチャレンジしました。

合宿までの準備

実りのある合宿にするために、準備は怠りません。

合宿参加者を開発領域(フロントエンド、バックエンド、アプリ、etc..)ごとにチームを分け、それぞれの領域の技術選定や設計をしてもらいました。

また、定期的に、合宿参加者が全員集まり、

  • 目指すべきMVP(Minimum Viable Product)はどういったものか

  • 合宿中に取り組むPBI(Product Backlog Item)は何か

について話し、意識のすり合わせを行いました。

箱根温泉「ホテルおかだ」

今年は箱根湯本にある「ホテルおかだ」さんにお邪魔しました。宿を決めるにあたり、株式会社Loco Partnersさんと株式会社FiNCさんのブログを参考にしました!この場を借りて御礼申し上げます。

relux 開発合宿 in 箱根湯本 - wootan's diary

FiNC 開発合宿 in 箱根! | FiNC Developers Blog

合宿の様子

f:id:jxpress:20191008062706j:plain

ロマンスカーに乗っていざ出発。

f:id:jxpress:20191008063003j:plain f:id:jxpress:20191008063015j:plain f:id:jxpress:20191008063027j:plain

宿到着。

f:id:jxpress:20191008063158j:plain

到着後、インフラチームから、デプロイ方法について説明を受けました。

弊社では、コード管理にGitLab、デプロイにGitLab CIを、普段は使っていますが、今回はGitHub Teamと、発表されたばかりのGitHub Actionsを使って、CI/CDのパイプラインを組んでみました。

f:id:jxpress:20191008145143j:plain

開発中。

f:id:jxpress:20191008063721j:plain

開発中。

f:id:jxpress:20191008145154j:plain

開発中。

f:id:jxpress:20191008063744j:plain

開発中。

f:id:jxpress:20191008063800j:plain

開発中。

f:id:jxpress:20191008145131j:plain

開発中。

f:id:jxpress:20191008063908j:plain

夕食・朝食は共にバイキング。種類豊富でした。

f:id:jxpress:20191008064133j:plain

ご飯から戻ってまた開発。深夜遅くまで続きました。

f:id:jxpress:20191008064148j:plain

開発中。

f:id:jxpress:20191008064212j:plain

そろそろ限界。

f:id:jxpress:20191008064313j:plain

合宿最終日は会議室を借りて、成果発表会を開きました。

f:id:jxpress:20191008145121j:plain

合宿の成果

最終日ギリギリまで粘ってなんとか、バックエンドからWebフロント・アプリまで疎通でき、3泊4日の日程内にプロダクトのrebuildができました!

WebフロントエンドはTypeScriptを、アプリはFlutterを使い、BFFとしてGraphQLのAPIを作りました。 バックエンドは、普段使ってるAWSではなくGCPを採用し、GCPの各種サービス(GAE、Cloud Functions、Cloud Pub/Sub)やKubernetes / Knativeにチャレンジしました。

まとめ

今回の合宿では、新しい技術にガッツリ取り組めました。また、3泊4日という期限内に、全員で1つのプロダクトの0→1開発に取り組めたことは、チームとして得難い経験になったと思います。

最後に、我々を快く受け入れてくれた「ホテルおかだ」さんに感謝!

www.hotel-okada.co.jp

JX通信社は「PyConJP 2019」にゴールドスポンサーとして協賛しています

VPoE の小笠原です。今年の PyConJP 2019 、JX通信社はゴールドスポンサーとして協賛しています! f:id:yamitzky:20190916100113j:plain

9/16(月)-9/17(火)の2日間、スポンサーブースに出展しています。ブースでは、Python で作った AI ピンポンゲーム大会を開催しています。

参加方法は、GitHub からフォークしてゲーム AI を実装するだけです! 高得点者には素敵な景品もありますので、ぜひ遊びに来てください! f:id:yamitzky:20190916101006j:plain

github.com

キャンペーン参加方法

PyConJP 2019 JX通信社ブースにてどなたでもご参加いただけます.
参加者は右上のボタンからこのレポジトリを Fork してください.
Fork したレポジトリに下記の手順で Pong チームを実装してください.
実装が終わったら弊社ブースにお越しください.
GitHub のアカウント名でエントリーできます.
プログラムでなく人力での参戦も可能です。ゲームの腕に自身がある方はぜひ参戦ください.
キャンペーンの終了は PyConJP のスポンサーブース終了時間に準じます.

こちらのピンポンゲームは社内勉強会で作ったものです。エンジニアブログもあわせてよろしくお願いいたします!

tech.jxpress.net

CloudWatch Eventsを使ってECSタスクを監視するツールをSAMで作る

こんにちは、SREエンジニアのたっち(@TatchNicolas)です。これまではPythonによるサーバサイド開発を担当していましたが、SREエンジニアとしてプロダクトを横断して安定性・パフォーマンス改善に取り組む担当になりました。

ヘルスチェックしにくいバッチ系のECSタスク

JX通信社では、ワークロードのほとんどをAWS LambdaまたはECSの上で動かしています。 Webサービス自体やAPIなどはALBやECSサービスのヘルスチェックを使って異常を検知したり、StatusCakeのようなサービスを使って外形監視をすることができます。

しかし、エンドポイントを持たないバッチ処理のようなタスクは、タスク自体が起動に失敗したり、途中で失敗した場合に上記の方法では検知することができません。

そこで、AWSの強力な機能の一つであるCloudWatch Eventsを使って、うまく動作しなかったECSタスクがあった場合にSlackへ通知できる仕組みをつくってみました。

CloudWatch Eventsとは

CloudWatch Eventsを使うと、イベントパターンを定義することでAWSリソースの変更をリアルタイムに取得してAWSのアクションをトリガーすることができます。 AWSリソースの変更だけでなく、スケジュールを組んで処理の自動化をすることも出来ます。 定期的に行いたいちょっとした処理をLambdaで書いてCloudWatch Eventsのcronで呼ぶ、みたいなことは実践していらっしゃる方も多いかと思います。

今回はイベントパターンを使ってECSタスクの状態変化を検知し、LambdaでSlackにアラートを投げてみたいと思います。

どうせやるならSAMで

監視のツール自体が秘伝のタレになってしまったり、依存しているAWSリソースがわからなくなってしまっては健全ではありません。

Lambdaの管理ツールはApex(記事執筆時点でメンテナンスがすでに終了)、Serverless Frameworkなど色々ありますが、今回はCloudWatch Eventsのイベントパターンもまとめて定義してしまいたいので、CloudFormationと親和性の高いSAMを採用します。

構成とコード

簡単な構成図とコードは以下のとおりです。

f:id:TatchNicolas:20190815142005p:plain
構成図

app/main.py

import json
import os

import requests

SLACK_WEBHOOK = os.environ['SLACK_WEBHOOK']
AWS_REGION = os.environ['AWS_REGION']


def lambda_handler(event, context):

    if event.get('source') != 'aws.ecs':
        print('Invalid event')
        print(event)
        return

    detail = event['detail']
    header = 'ECSタスクが異常終了または起動に失敗しました\n'

    cluster_name = detail['clusterArn'].split('/')[1]
    task_id = detail['taskArn'].split('/')[1]
    task_def = detail['taskDefinitionArn'].split('/')[1]

    url = f'https://{AWS_REGION}.console.aws.amazon.com/ecs/home?region={AWS_REGION}#/clusters/'\
        f'{cluster_name}/tasks/{task_id}/details'

    text = '\n'.join([
        header,
        f'クラスタ: *{cluster_name}*',
        f'タスク定義: *{task_def}*',
        url,
        '```',
        json.dumps(detail, indent=2),
        '```',
    ])
    attachments = {
        'text': text,
        'color': 'danger'
    }

    payload = {
        'attachments': [attachments]
    }
    requests.post(SLACK_WEBHOOK, json=payload)

template.yaml

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  sam_python37

  Sample SAM Template for Python 3.7

Globals:
  Function:
    Timeout: 3

Resources:
  ECSMonitor:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: app/
      Handler: main.lambda_handler
      Runtime: python3.7
      Environment:
        Variables:
          SLACK_WEBHOOK: https://hooks.slack.com/services/xxxxxxxx/xxxxxxxx/xxxxxxxxxxxxxxxxxxxxxxx
      Events:
        CommandNotFound:
          Type: CloudWatchEvent
          Properties:
            Pattern:
              !Sub |
              {
                "detail-type": [
                  "ECS Task State Change"
                ],
                "source": [
                  "aws.ecs"
                ],
                "detail": {
                  "clusterArn": [
                    "arn:aws:ecs:ap-northeast-1:123456789012:cluster/cluster-to-monitor-a",
                    "arn:aws:ecs:ap-northeast-1:123456789012:cluster/cluster-to-monitor-b"
                  ],
                  "containers": {
                    "exitCode": [
                      1,
                      127
                    ]
                  }
                }
              }
        TaskFailedToStart:
          Type: CloudWatchEvent
          Properties:
            Pattern:
              !Sub |
                {
                  "source": [
                    "aws.ecs"
                  ],
                  "detail-type": [
                    "ECS Task State Change"
                  ],
                  "detail": {
                    "clusterArn": [
                        "arn:aws:ecs:ap-northeast-1:123456789012:cluster/cluster-to-monitor-a",
                        "arn:aws:ecs:ap-northeast-1:123456789012:cluster/cluster-to-monitor-b"
                    ],
                    "stopCode": [
                      "TaskFailedToStart"
                    ]
                  }
                }

template.yaml のほうも、リージョン名やAWSアカウントの部分も疑似パラメータにできそうですが 面倒くさかった わかりやすさを優先してハードコードしちゃってます。

実装していてハマったのは、CloudFormationでイベントパターンを定義しているところです。

最近はCloudFormationをJSONで書くケースは稀かもしれませんが、イベントパターンはYAMLで書いた場合に意図せぬ挙動をすることがあります。

YAMLではクオートをつけない文字列は数値として扱われるのが基本ですが、コンテナの終了コードをイベントパターンに定義する場合、デプロイした際に数値型が文字列型に変換されてしまいます。

つまり、以下のようなイベントパターン定義がCloudFormation中にあっても、

# (略)
EventPattern:
  source:
  - aws.ecs
  detail-type:
  - ECS Task State Change
  detail:
    clusterArn:
    - arn:aws:ecs:ap-northeast-1:123456789012:cluster/samplestack
    containers:
      exitCode:
      # クォートで囲っていないので数値として扱われることを期待
      - 1
      - 127
# (略)

このようなJSONとして、exitCode の値が数値型でなく文字列型としてCloudWatch Eventsに設定されてしまいます。

{
  "detail-type": [
    "ECS Task State Change"
  ],
  "source": [
    "aws.ecs"
  ],
  "detail": {
    "clusterArn": [
      "arn:aws:ecs:ap-northeast-1:123456789012:cluster/samplestack"
    ],
    "containers": {
      "exitCode": [
        "1",
        "127"
      ]
    }
  }
}

すると、CloudWatchが発行するイベントJSONではexitCodeは数値型で渡されるので、イベントパターン定義と比較されるときに 127"127"イコールにはならず、イベント発火の条件にマッチしない結果になってしまいます。 しかし、テンプレート自体をJSONで書くのもツラいので、前述のサンプルの通りイベントパターンの部分のみをJSONで書くようにして回避できました。

また、記事執筆時点でワイルドカードや否定の条件には対応していないようでした。 そのため、「終了コードが0以外」のような条件を指定するのはやや難しいようです。

上記の例ではCloudWatch側でコンテナの終了コードを条件として指定し、Lambdaの発火をコントロールしました。 コストを受け入れられるのであればイベントパターンの条件をクラスタARNのみにして、Lambda側でイベントで送られてくるJSONの内容を元にSlack通知するかどうかを判断させるパターンでも良いかもしれません。

まとめ

今回はすでに利用中のタスク定義に手を加えずにタスクの成功/失敗を取得するような簡単な監視の仕組みを作ってみました。

次回は、サイドカーコンテナを使って(=タスク定義に手を加えて)より詳細な監視ができる仕組みを作ってみようと思います。

参考資料

  • Amazon CloudWatch Events イベントパターン
  • AWS CLI test-event-pattern
    • イベントのJSONとイベントパターンのJSONを入力として、マッチするかどうかをチェックできます。
    • 実施にイベントを起こしてAWS上で検証すると時間もお金もかかってしまうので、取りたいイベントのJSONを保存しておいて手元で検証するのが良いでしょう。

PostgreSQL 版 Aurora Serverless を開発環境で使ってみた

こんにちは、Pythonエンジニアの @kimihiro_n です。 今回はこの前リリースされた PostgreSQLの AWS Aurora Serverless を使ってみたという話を。

TL;DR

  • Auroraと比較してだいぶ安い
  • オートスケール便利
  • 立ち上げは結構遅い
    • 開発環境用途には便利
    • 本番はキャパシティを0にしない工夫が必要

Aurora Serverless とは

f:id:nsmr_jx:20190722203232p:plain:w300

MySQL、PostgreSQL と互換性がある AWS 製のリレーショナルデータベースとして Amazon Aurora というサービスがあります。 通常の MySQL などとと比べて高いパフォーマンスが出るよう独自にチューニングされており、マネージドサービスとしてインスタンスをあまり意識せず利用することができます。 またデータベースのサイズが自動で拡張する ( 64TB まで) ので、ディスク容量を心配せず利用できるのも大きな特徴ですね。

AuroraServerless はこの Aurora をさらに進化させたタイプのデータベースです。 Aurora の場合、マネージドサービスとはいえ起動しておくインスタンスのスペックや個数は自分で管理する必要があります。 利用負荷に応じてインスタンス数が自動で増減するわけではないので、使う量に応じてインスタンスを手動調整したり、オートスケールを設定したりする必要があります。 また Reader、Writer の概念があるため Writer の負荷に注意するなどといったことも大切です。

AuroraServerless では処理能力がキャパシティという単位で抽象化されており、キャパシティ数を変更することで柔軟にスケーリングすることが可能です。 デフォルトでオートスケールが組み込まれており、最低キャパシティと最大キャパシティ、それから利用がないときはキャパシティを0にするかどうかだけを設定すれば利用することが可能です。

この前まで MySQL 版でしか AuroraServerless に対応していなかったのですが、先日 PostgreSQL の Serverless も一般公開され自由に使えるようになりました!

https://aws.amazon.com/jp/rds/aurora/serverless/

PostgreSQL 版 Serverless を使ってみる

現在、開発環境と本番環境で PostgreSQL 版の Aurora データベースを利用しているのですが、Serverless 版が出たので早速使ってみることにしました。 使ってみたかったという動機はもちろんありますが、 Serverless を利用する大きな理由がコストですね。

Aurora データベース、インスタンスの値段が結構高く、開発環境で使うにはコストが嵩んでいました。 特に PostgreSQL 版だと MySQL 版より最低限必要なインスタンスのスペックが大きくつらみがありました。 これを Serverless 版に変更することで、最低ラインの費用を下げたり、利用されない深夜帯・休日の間のコストを0にしたりといったことが可能になります。

  • Aurora

    0.35($/instance/hour) * 1(instance) * 24(hour/day) * 30(day/month) = 252 ($/month)

  • Aurora Serverless

    0.1($/cap/hour) * 2(cap) * 24(hour/day) * 30(day/month) = 144 ($/month)

最低限の状態で24時間・30日動かした場合の費用比較です。どちらも東京リージョンで、Auroraは r5.large の1台構成での費用です。 PostgreSQL を Serverless で動かす場合最低 2 キャパシティとなっていました。 (MySQL 版は 1 キャパシティから)

ずっと動かす前提でも半額くらいになるみたいです。月100ドルの差は結構大きいですね。 開発環境の場合使わない時間帯のほうが大きいのでここから更に安くなりそうです。

Aurora からの移行

移行作業自体はスムーズでした。

  1. Aurora のデータベースを pg_dump コマンドでバックアップする
  2. 新しく Serverless でデータベースを起動させる
  3. ダンプしたデータを流し込む
  4. データベースの接続先をつなぎ替える

で完了しました。

同じ Aurora ファミリーだから RDS のスナップショットから Serverless 版起動できるかなと思ったのですが、バージョン違いからか Serverless の選択肢がグレーアウトしており、手動でデータを流し込む必要がありました。

特記すべきは Serverless 版には Reader・Writer の区別がないことですね。 アプリケーション側で使い分けの処理入れてましたが、区別ないので同じエンドポイントを両方に指定してます。

動作検証

移行できたようなので動作確認へ。 Django のアプリケーションで利用していて、一通りの機能を試してみましたが問題なさそうでした。 PostGIS のような拡張機能もちゃんと動いています(すんなり動いてしまって逆に怖さがあります)。

未利用時の挙動

一番気になるのが未利用時の挙動ですね。 DBへのリクエストがしばらくない場合、キャパシティを0にすることができます。 この状態であればストレージに対する課金のみでデータベースを維持する事ができます。

f:id:nsmr_jx:20190722192135p:plain

アプリケーションサーバーにアクセスしないで放置した状態でのキャパシティの変化。 2キャパシティで動いていたのが自動で0キャパシティに変化してますね。問題なさそうです。

逆にこの状態でアプリケーションサーバーにアクセスするとどうなるでしょうか。 キャパシティが0、つまりDBサーバーが停止している状態でアクセスするので、DB接続エラーになってしまわないかが心配です。

f:id:nsmr_jx:20190722192153p:plain

DBへのリクエストによってキャパシティがもとに戻る図。 接続エラーが出ることなくキャパシティが無事に戻りました。 ただやはりキャパシティが0から立ち上がるには少し時間がかかります。 状況にもよりますが10~30秒ほどかかっている気がします。 実際のユーザーがいる環境ではあまり起こってほしくない待ち時間ですね。

キャパシティ0でないときのレスポンスは概ね良好でした。ただ時々詰まったような挙動をすることがあります。 (アプリケーションサーバーを経由しての挙動確認なので、発行するクエリが一時的に多くて、用意していたキャパシティに対する処理が不足していたとかかもしれません。※ 要調査)

f:id:nsmr_jx:20190722192215p:plain

Serverless 切り替え前と後の平均レスポンスの変化。

グラフが跳ね始めてるのがちょうど切り替えのタイミングです。 開発環境なのでリクエスト総数がそんなになく、1つ遅い応答時間があると平均も跳ねてしまうという点は留意して見る必要があります。 キャパシティが0になってしまうと待ち時間が多く発生してしまうので、本番環境で Serverless を導入する際はどうキャパシティを制御するかが鍵になりそうですね。 開発環境用であれば多少待つくらいで済むので問題なさそうです。

おわりに

Serverless のおかげでデータベースを安く楽に管理できるのはありがたいですね。 待ち時間を気にしなくてよく、常日頃可動しているわけではないバッチ処理系とも相性が良さそうです。 サーバレスなデータベースの選択肢の一つとして活用してみてはどうでしょうか。

Flutterでモブプログラミング会を(12人で)やってみました

JX通信社のアプリエンジニアのぬまっち(@numatch2552)です。普段はAndroidエンジニアをやっています。 そして1年ほど前に技術書ビブリオバトルの記事を書いた者です。

早いものであの記事から1年が経ちました。今回も自分が担当する社内勉強会ではちょっと変わった趣向で開催してみました!

高まるFlutterの機運

昨年末に安定版がリリースされたFlutterですが、社内でも機運が高まっていました! さらにアプリエンジニアとして、業務で使う事もあって自分として知見もあったので 勉強会のメインに選定しようと思いました。

しかし新しいプラットフォームでの勉強会って、参加者の環境構築で躓くことが多いですよね。 かくいう自分も経験があります。 最初はFlutterのハッカソンを考えていましたが、Flutter未体験の参加者が多い状態なので頭を悩ませました。

モブプログラミングでやってみよう

そこで思いついたのは、モブプログラミングと掛け合わせることでした。 モブプログラミングとは下記の記事を参考にさせて頂きました。↓↓↓
まだ1人でコード書いてるの?モブプロを1日中、3週間毎日続けて得た知見 | Kurashicom Engineers' Blog

第一に勉強会なので、業務のようにスピード優先というものはありませんし、 知見の共有という点ではモブプロが最適に思えました。 モブプロも初めての実践でしたが、「やればきっと出来る!」くらいの勢いで挑みました(笑)

どこまでを目指すのか

社内勉強会は2時間なので、その範囲でどこまで作るのかを事前に考えようと思いました。 しかもモブプロ未経験者が集まって進めるので、スピードは上がらないことが予想されます。

その上で Flutterってこういう作りなんだ! という体験はしてもらいたかったので、 下記の仕様を考えました。

  • 全部で2画面
  • 最初の画面(main.dart)
    • 画面中央にテキスト・ボタンがある
    • ボタンの押下で2番目の画面に遷移する
  • 2番目の画面(second_page.dart)
    • FloatingActionButtonがある
    • FABが押下されるとリストが描画される
    • FABが押下される度にリストの項目が増えていく

当日イメージしやすいように、事前にこちらで簡単なアプリを作成しておきました。 モブプロに入る前に参加者に見て貰うことで、どこまでをゴールにするかが明確に伝わったかなと思います。

勉強会当日

12人によるモブプログラミング

当日、勉強会を開催する時間になると参加者が集まってきました。 その数12人!(事前に把握しておけよ、と)

経験者を除くと10人が未経験者でした。 これでモブプログラミングが成り立つのかと正直焦りましたが、もうやり遂げるしかありません。

キーボード問題

始めようとしてすぐに問題が発覚です。 キーボードには USキーボードとJISキーボードがあるのです。

JX通信社では好きな方のキーボードを選ぶ事ができます。(ちなみに自分の業務用MacBookはUSキーボードです。) 自分のMacBookをみんなで回していけばモブプログラミング出来るだろう、と考えていましたがJIS派のエンジニアもいるので、頭を抱えました。

ここでメンバから「複数のワイヤレスキーボードを接続すればいける」と助言を頂いて助かりました。 初っ端から躓きましたが、社内から両方のキーボードを揃えてきてスタートです。

最初の画面を作る

基本的に経験者はノータッチですが、本当に詰まった時は助言する形で進めました。

「Widgetって何だろう」 「とりあえずMaterialAppってのを使うといいみたい!」

main関数から最初を画面を呼び出しところから始まり、ツールバーを表示する方法などをみんなで調べながら進めていました。

f:id:numatch-jx:20190711212724p:plain
勉強会風景

みんなDart言語も初めてなので、コンパイルエラーの解決方法も一苦労です。

「ここでreturnが必要なのはなのは何でだろう?」 「何かが足りないってエラー出てますね。セミコロンかも。」

(言語の好みの話題も出てきて、個人的に興味深かったです。)

ドライバーを5分交代で次々と変えていきながら、ナビゲーターがサポートする形で進めていきました。

やがてStatelessWidgetScaffoldWidgetを使って最初の画面が形作られます。

ButtonWidgetの実装で詰まった時の事が印象的でした。 一口にButtonのWidgetといってもFlatButtonIconButtonなど種類がありますが、 みんなで意見を出し合って仕様を満たすRaisedButtonが選ばれました。

押下後の動作についての実装もWebメインの方だとイメージが湧きにくかったようですが、 経験者がアロー関数で実装する方法を紹介して最初の画面が完成します。

二番目の画面の実装

次の画面では FloatingActionButtonが押される度に画面に表示されるリストの内容が更新される という目標があります。 ここでは Flutterの要素でも重要な StatefulWidget を使う必要が出てきます。

だんだん参加者の方たちも、最初の画面よりは慣れてきたのか進みが早くなってきましたが、 FloatingActionButtonの実装や、表示するList内容の定義まではすんなり迷う事なく進んでいたと思います。

Button押下時の実装方法は前の画面で分かってきましたが、中々画面の表示を更新させる方法が分からずにドライバーが交代していきました。

経験者が助言しなければ厳しいのではないか、とも思っていました。 ですが、9人のナビゲーターが調べていく中で助言なしに StatefulWidgetsetState()を組み合わせることでbuildが再度コールされる事に辿り着きました。

f:id:numatch-jx:20190708213527j:plain
勉強会風景2

画面表示が更新された時、自然と歓声が湧き上がったのがとても印象的でした。 ナビゲーターが知見を共有しあって実装の解を見つけ、みんなで進捗を祝う。まさにモブプログラミングの醍醐味を味わえた瞬間だったと思います。

そして、2時間という決まった枠組みの中で目標としていた仕様を満たしたアプリが出来上がりました。

f:id:numatch-jx:20190708213044g:plain
完成したFlutterアプリ

開催してみて

正直始まるまでは上手く進行できるのか、決められた枠組みの中で目標としたアプリが出来るのか、不安でいっぱいでした。

サーバーサイドエンジニアやフロントエンジニアなど、普段ネイティブアプリ開発とは離れた業務をされてる方も沢山参加頂きましたが、 抜群のチーム開発力を発揮して、みんなでモブプログラミングを進めることが出来て嬉しかったです。

終わってみればJXエンジニアのメンバがFlutterに対して興味津々であったことに、非常に助けられたと思います。

FlutterのWidgetを元とした構成のおかげで、複数のファイルを行き来することなく一つのファイルで画面を構成することが出来ることも モブプログラミングとの相性が良いのではないか?と感じる事も出来ました。

改めてFlutterの可能性も再発見出来たので、嬉しく感じる勉強会になったと思います。