CYDAS Developer's Blog

サイダス技術者ブログ

PHP5系 から 7系に上がって出来なくなること

こんにちは。技術部の金尾です。
PHPといえば良くも悪くもゆるい書き方を許してくれる大らかな言語ですが、7系以降では お行儀のわるい書き方には(以前よりは)厳しくなりました。
廃止されたものやルールが変わった部分もあるのでまとめておきます。

split関数の廃止

split関数が廃止されました。以降はpreg_splitexplodeを使う必要があります。

<?php
// 呼び出しても undefined
var_dump( split(',', '1,2,3,4') );
//=> Fatal error: Uncaught Error: Call to undefined function split() in ...

// explode などに置き換えましょう。
var_dump( explode(',', '1,2,3, 4' ) );
//=> array(4) {
  [0]=>
  string(1) "1"
  [1]=>
  string(1) "2"
  [2]=>
  string(1) "3"
  [3]=>
  string(2) " 4"
}

E_STRICT 通知の深刻度の変更

PHP7以降から E_STRICT 通知が廃止されました。それに伴い今まで E_STRICT だったものは E_NOTICE E_WARNING E_DEPRECATED のいずれかに変更になります。E_ALL & ~E_STRICT でひとまず非表示にしておく、ということができなくなったのでその場合対応が必要です。
いくつかある変更対象のエラーのなかで、比較的うっかりやりがちな Signature mismatch during inheritanceOnly variables should be passed by reference について取り上げます。

Signature mismatch during inheritance

オーバーライドしたメソッドのシグネチャが継承元と異なる場合、E_WARNING エラーが発生します。

<?php
class Hoge {
    public function method(){
        return 'hoge';
    }
}

class ChildHoge extends Hoge {
    // 親の method とは異なる引数を指定してオーバーライド
    public function method($foo){
        return 'hoge' . $foo;
    }
}
//=> Warning:  Declaration of ChildHoge::method($foo) should be compatible with Hoge::method() in ...

Only variables should be passed by reference

引数に参照渡しを要求している関数にメソッドの戻り値を直接渡すと E_WARNING エラーが発生します。 array_poparray_shiftなどを使用する際には注意が必要です。

<?php
// 関数の戻り値を直接渡しては×
var_dump(array_pop( explode(',', '1,2,3,4') ) );
//=> Notice:  Only variables should be passed by reference in ...

// 一度変数に格納すればok
$hoge = explode(',', '1,2,3,4');
var_dump(array_pop($hoge));
//=> string(1) "4"

変数やプロパティ、メソッドへの間接的なアクセスの扱いの変更

$$foo['bar']$foo->$bar['baz'] といった、
変数やプロパティ、メソッドへの間接的なアクセスを厳密に左から右の順で評価するようになりました.

PHP5 PHP7
$$foo['bar']['baz'] ${$foo['bar']['baz']} ($$foo)['bar']['baz']
$foo->$bar['baz'] $foo->{$bar['baz']} ($foo->$bar)['baz']
$foo->$bar'baz' $foo->{$bar['baz']}() ($foo->$bar)'baz'
Foo::$bar'baz' Foo::{$bar['baz']}() (Foo::$bar)'baz'

このため可変変数や可変メソッドを使用するときには5.x系とは異なる挙動をすることがあります。

<?php
class Foo {
    public function bar(){
        echo "bar";
    }
    public function baz(){
        echo "baz";
    }
}
$arr = ['bar' => 'bar', 'baz' => 'baz'];

// PHP5.x系では $arr['bar'] が先に評価されていたが、
// PHP7.x系では (new Hoge)->$arr が先に評価されるためとエラーになる。
(new Foo)->$arr['bar']();
//=> Notice:  Array to string conversion in ...
//=> Notice: Undefined property: Foo::$Array in ...

// 波括弧を使用して $arr['bar'] を先に評価するよう指定すればok
(new Foo)->{$arr['bar']}();
//=> "bar"

無効な文字列による算術演算の通知

PHP7.1以降、数値形式ではない文字列を使って算術演算を行おうとしたときに E_WARNING あるいは E_NOTICE エラーが発生するようになりました。キャストしてくれることをいいことにうっかり空文字を0として扱ってしまうとバンバン Warning エラーが出ます。

  • E_NOTICE になるケース 数字で始まり、数値以外で終わる文字列のときに発生
    Notice: A non well formed numeric value encountered in ...

  • E_WARNING になるケース 数値が含まれない文字列のときに発生
    Warning: A non-numeric value encountered in ...

<?php
var_dump("0" + 1);
//=> int(1)

var_dump("01" + 1);
//=> int(2)

var_dump("" + 1);
//=> Warning:  A non-numeric value encountered in ...

var_dump("1m" + 1);
//=> Notice:  A non well formed numeric value encountered in ...

var_dump("m1" + 1);
//=> Warning:  A non-numeric value encountered in ...

var_dump("1m" + ""); 
//=> Notice:  A non well formed numeric value encountered in ... 
//=> Warning:  A non-numeric value encountered in ...

【AWS SAA合格への道】EC2

AWS ソリューションアーキテクト - アソシエイト合格に必要な知識をまとめていきたいと思います!
今回は「EC2」になります。

EC2(Elastic Compute Cloud)

AWS上で動く仮想サーバー

EC2の料金体系

オンデマンドインスタンス

EC2を起動している時間だけ料金が発生する(従量課金方式

利用用途

  • 前払いや長期間の契約なしで、EC2の低コストや柔軟性を利用したいと考えているユーザー
  • 短期間、スパイクを伴う、または予測不能な作業負荷があっても中断できないアプリケーション
  • EC2で開発またはテストするアプリケーション

リザーブドインスタンス

1年・3年の年単位でEC2を契約することで、オンデマンドより割引が受けられる(長期契約方式

利用用途

  • 定常的に使用するアプリケーション
  • キャパシティーを予約する必要があるアプリケーション
  • コンピューティングの合計コストを削減するために、EC2 の 1~3 年間の使用を確約できるお客様

スポットインスタンス

オークション形式で使用されていないEC2を入札し、自分が入札した価格より高く入札したユーザーが出たときなどに
使えなくなる代わりに安い値段で利用できる(入札方式

利用用途

  • 開始および終了時間が柔軟なアプリケーション
  • 非常に低額のコンピュート価格でのみ実行可能なアプリケーション
  • 大容量の追加キャパシティーのために、緊急のコンピューティングニーズを有するユーザー

Dedicated Hosts

物理ホストサーバーを専有し、その上でEC2を起動できる

  • Windows Server、SQL Server、SUSE Enterprise Server などのソフトウェアライセンスを使用できる(BYOL)
  • 物理ホスト単位で課金(物理ホストの上で動くEC2に対しては課金されない)
  • 管理機能が豊富でインスタンスの詳細のコントロール、リソースの可視化、CPUコアやソケットレベルの管理が可能

利用用途

  • 専有サーバーのソケット数または物理コア数に応じたライセンス数が必要な場合
  • コンプライアンスや規制要件

ハードウェア専用インスタンス

物理ホストサーバーを専有し、その上でEC2を起動できる

  • 一部のライセンスのみサポート
  • インスタンス単位で課金

利用用途

  • 専有サーバーのソケット数または物理コア数に応じたライセンスは必要ない場合
  • コンプライアンスや規制要件

プレイスメントグループ

インスタンス間における低レイテンシな通信実現するための機能

クラスタプレイスメントグループ

単一AZ内のインスタンスを論理的にグループ化したもの

  • EC2間で低ネットワークレイテンシー、高ネットワークスループットが発生する場合に最適
  • 複数のAZにまたがることはできない

パーティションプレイスメントグループ

論理的なパーティションに分散されているインスタンスのグループ

  • EC2で実行する HDFS、HBase、Cassandra といった分散され、レプリケートされた大規模なワークロードに最適
  • 複数のAZに分散させることが可能

スプレッドプレイスメントグループ

それぞれ異なるハードウェアに配置されるインスタンスのグループ

  • 少数のEC2が互いに分離して保持される必要があるアプリケーションに最適
  • 複数のAZにまたがることが可能

セキュリティ

責任共有モデル

f:id:aym413:20190227213618p:plain
AWSが提唱する責任共有モデル

【AWSの責任範囲】

AWS クラウドで提供される インフラストラクチャーの保護 に責任を負います。
インフラストラクチャーは AWSが持つハードウェア、ソフトウェア、ネットワーク、設備 を指します。

  • 物理的な施設や設備、セキュリティ
  • ハイパーバイザー
  • ネットワークインフラ
  • 仮想インフラ

【利用者の責任範囲】

  • OSやミドルウェアの管理 (更新やセキュリティパッチを含む)
  • インスタンスにインストールしたアプリケーションソフトウェアの管理
  • セキュリティグループの管理
  • 適切なネットワークの設定
  • データの暗号化
  • アカウントの管理

セキュリティグループ

EC2へのトラフィックを制限するファイアーウォールの機能

  • デフォルトでは全てのインバウンドトラフィックが遮断されている
  • 接続を許可するIPアドレス または セキュリティグループをインバウンドやアウトバウンドのルール適用する(ホワイトリスト型
  • アウトバウンド または インバウンドで許可した通信の戻りのトラフィックについては、許可をしなくても受信できる(ステートフル
  • 全てのルールが適用される
  • ルールの変更はすぐに反映される

ネットワークACLとの違いに注意!

ネットワーク

EIP

EC2に割り当てる固定のパブリックIP

  • 停止/起動しても割り当てはそのまま
  • EC2を停止していると課金が発生する

設定

メタデータ

インスタンスの起動時にコマンドを渡すことができる

ユーザーデータ

インスタンスのAMI、パブリックIP、ホスト名など、EC2の各種情報を取得することができる

ストレージ

インスタンスストア

ホストコンピュータにーに内臓されている 揮発性 ストレージ

f:id:aym413:20190227231210p:plain
インスタンスストア のイメージ

  • 揮発性のため、EC2を停止/起動すると インスタンスストア上のデータは削除 される
  • ただし、再起動では削除されない → 再起動では物理ホストが変わらないため
  • 内臓ディスクのため、高速なI/Oが可能
  • インスタンスストレージやエフェメラルストレージと呼ばれることもある
  • 利用用途: 一時的な計算処理やキャッシュデータ、高速なI/Oが必要な場合など

EBS(Elastic Block Store)

EC2とは独立して管理される 永続的な ストレージ

f:id:aym413:20190227231411p:plain
EBSのイメージ

  • EC2とは独立しているため、EC2を停止/起動してもデータは保持されたまま
  • 任意のAZ(アベイラビリティゾーン)に作成可能
  • スナップショット(バックアップ)を取得すれば、他のAZにEBSを作成可能
  • スナップショットは初回フルバックアップ、その後は差分のみ取得される

EBS最適化オプション

通常のネットワークとは別にEBS専用のネットワーク帯域を確保し、ディスクパフォーマンスを安定させるオプション

f:id:aym413:20190227233656j:plain
EBS最適化オプションのイメージ

EFS(Elastic File System)

複数AZの最大数千のEC2から同時接続可能なファイルストレージサービス(概要レベルでOK)

バックアップ

AMI(Amazon Machine Image)

EBSの中のデータ(スナップショット) とEC2を構成する管理情報を含む起動テンプレート

  • AMIを取得することで所有するEC2のバックアップが取れる
  • 既に公開されているAMIを使用してEC2を起動することも可能

【イベントレポート】JAWS DAYS 2019 に参加してきました!

f:id:aym413:20190228110320j:plain こんにちは!エンジニアのこぶかた@kobuuukata)です!

2019年2月23日(土) 五反田TOCメッセで行われた JAWS DAYS 2019 に参加してきました!
今年は来場者がなんと! 1900人!?
申し込み時点では上限の2300人を超えて、キャンセル待ちも出ていたようです・・すごい!

そんな中、サイダスも企業スポンサーとして参加!

f:id:aym413:20190228090150j:plain
サイダス見つけられましたか?

ボランティアスタッフとしての活動

私は当日ボランティアスタッフとして参加してきました!
ボランティアスタッフでも色々と役割があるのですが、
今回はセッショントラックと懇親会補佐を担当してました。

ボランティアスタッフは以前もやったことがあるのですが、
セッショントラックのお手伝いは初めてでした。

セッショントラックのスタッフとして主にやったこと

  • 参加者に詰めて座ってもらうように誘導
  • 各セッションの登壇者の名前とセッションタイトルの確認
  • 各セッション開始・終了時のアナウンス
  • タイムキーパー
  • 随時、必要なアナウンスを行う(忘れ物やランチの案内など)

これを2名で分担してやります。

セッショントラックのスタッフをやってみて

  • 役割上、一番前の席で聞けるというのは特権!

  • でも、本当に見たいセッションはスタッフのシフトを入れないようにする

スタッフのシフトを決める時、自分の聞きたいセッションを選んでいいよ
と言われて自分のシフトを組んでみたのですが、

セッション中は 時間を気にしなければいけなかったり・・
次のセッションを確認したり・・
何か連絡がきていないかSlackを確認したり・・
直前まで登壇者が現れず、ヒヤヒヤすることもしばしば・・

セッショントラックのスタッフをやっていると
なかなかセッションに集中することができませんでした。
(初めてだからというのもあるかもしれないですが)

なので、本当に聞きたい!というセッションがあれば
スタッフのシフトを入れない方がいいのかなと思いました。

  • すごい緊張する(笑)

ちょっと始めに挨拶するだけだからと思っていたのですが、
大勢のお客さんを前にすると緊張して
セッションタイトルをカミカミになってしまったことも・・登壇者のみなさんすみません!(泣)
次回の教訓としては、
自分が担当するセッションはわかっているので、
事前に練習しておくと本番落ち着いてできそうです!

  • 携帯のモバイルバッテリーは必須!

当日の連絡はSlackで行われ、随時情報が流れてきます。
かなり電池が消耗するので必須です。私は忘れて知り合いに借りました。。

懇親会補佐

懇親会補佐ってなに?って感じですが(笑)、
司会が困った時に助けたり(多分何もできてなかったと思う・・すみません)
いきなりAWS SUMIRAIの授賞式の盾を持ってきてと言われたり
まあそんな感じで、ほとんど私の役目はなかったのですが・・
これまた懇親会スタッフの特権で一番前で見させてもらいました!感謝!!

セッションのまとめ

当日のセッション資料をまとめてくれている方がいます!神!!
聞けなかったセッションはこちらで見てみようと思います。
qiita.com

JAWS DAYSを通じて

私個人としては今回を含め、6回目の参加となりました。
最初は一般参加者として、セッションを聞いて学びを持ち帰るだけでしたが、
これまでJAWS DAYSやJAWS-UGのコミュニティを通じて多くの方に巻き込まれ(たくさんの機会をいただき)、
ボランティアスタッフや登壇、海外コミュニティでのスピーカーなどなど、様々な経験をさせてもらいました。
それが今の私にとってとても大きな価値になっています。
コミュニティに参加することでこんなにも変化していくとは想像もしていませんでした。
そういった方が少しでも増えると嬉しいなと思います。
また来年も何かの形でJAWS DAYSに関わり、いろんな人を巻き込んでいきたいと思います!

そして今回はスタッフとして参加させてもらっていたので、朝から夜までバタバタしていて
久しぶりにお会いできた方もいたのですが、ゆっくりお話できず・・。
またどこかの勉強会でお会いしましょう〜!

f:id:aym413:20190228110033j:plain

【後編】Vue CLI + Laravel によるMSPA (Multi-Single Page Application)

f:id:kfukuyama:20190225131307p:plain

こんにちは、福山健@kenfdev)です!

この記事は 【前編】Vue CLI + Laravel によるMSPA (Multi-Single Page Application) の続きになります。 前編 を読んでいることを前提としているのでご注意ください。

長すぎて読みたくない人へ

コードを見たほうが早い!って人は以下にこの記事に関連するリポジトリを公開しているので遊んでみてください。

github.com

後編でやること

後編ではMSPA(Multi-Single Page Application)の部分に着目します。

  • MSPAについて
  • app2 を作ってみる
  • まとめ

という内容でお届けします。

MSPA(Multi-Single Page Application)とは?

「MSPA MSPA」って言ってますけど、たぶんMSPAという正式な略語は無いと思います。SPA(Single Page Application)と違って、1ページ毎にサーバーがレンダリングするようなアプリケーションをMPA(Multi Page Application)と呼ぶことはあります。でも、この記事で作るものはただのMPAでは無く、 複数のページでそれぞれのSPAがある のでMSPA(Multi-Single Page Application)と呼ばせていただきます。

以下の悩みがある人にはMSPAがフィットするかもしれません。

  • 一つの巨大なSPAを作りたくない。
  • ドメインが明確に分かれている機能間の画面遷移までニュルニュル遷移する必要がないからSPAを分けたい。
  • 複数チームで分かりやすく、SPAを分担して開発したい

でもMSPAを作ろうと思った場合に悩みもあります:

  • 複数のSPA作りたいけど、できればパイプラインは統一したい(configの冗長管理したくない)
  • みんな同じ外部ライブラリ使うのに冗長なpackage管理したくない
  • 安易な共通化は危険。。。と理解しつつも共通利用できるものは複数のSPAから同じオレオレライブラリっぽいところから使いたい

通常であれば↑を真面目に考えないといけないのですが、Vue CLI を使うとこれらを意識せずにいい感じに管理してくれる pages という機能があります。 前編 ではこの機能を既に使わせていただいているのですが、単一のSPAしか作っていないという意味のない状態になっています。後編ではここにもう一つSPAを加えてMSPAにしましょう!

【余談】 上の「悩み」のところにもあるように、安易な共通化は長い目で見たときに負債になる可能性が高くなります。外部ライブラリの共通化も、本気でマイクロサービス化などでアプリを分けた場合には「独立したデプロイ」ができなくなるので、注意が必要です。ただし、それらを理解した上で、いざ別のアプリ(コードベース)に切り出すときには pages 機能でVueのアプリを切り出すのは比較的容易に行えるはずです。

構成

後半の構成は次の通り。Vueのアプリが増えています。MSPAのコンセプトを見せたいだけなので、2つ目のVueアプリは特に処理を入れません。(最初のアプリ同様、API連携は同じように作っていくことができます)

f:id:kfukuyama:20190224205558p:plain
構成

フロントエンド app2 の作成

名前が安易すぎますが、2個めのSPAを app2 とでもしましょう。中身というよりMSPAのコンセプトだけ見せたいので、ベースは app1 から作っちゃいます。

# frontendディレクトリ内で作業します
cd frontend

# app1をapp2としてコピー
cp -r src/app1 src/app2

app2 からはrouter機能を消しちゃいましょう。

rm src/app2/router.js

# 合わせてコンポーネントもApp以外消してしまう
rm src/app2/views/*
rm src/app2/components/*
// src/app2/main.js

import Vue from 'vue'
import App from './App.vue'
// import router from './router'  ←消してしまう

Vue.config.productionTip = false

new Vue({
  // router,  ←消してしまう
  render: h => h(App)
}).$mount('#app')

Vuetifyを適用

このまま app2 を表示しても面白くないので、無駄に Vuetify を使って見た目をドラスティックに変えてみちゃいましょう。まずはVuetifyインストールしましょう。

npm install vuetify

VuetifyをVueで使うように指示します。

import Vue from 'vue'
import App from './App.vue'
import Vuetify from 'vuetify'  // ←ここ
import 'vuetify/dist/vuetify.min.css'  // ←ここ

Vue.use(Vuetify)  // ←ここ

Vue.config.productionTip = false

new Vue({
  render: h => h(App)
}).$mount('#app')

iconも使えるようにしておきたいので、 index.html<head> にiconへのリンクを追加したいのですが、せっかくなので templates/base.html ではなく、Vuetifyアプリ用のテンプレートを用意しましょう。仮に 前編 で作った app1 でも使っている templates/base.html に追加してしまうと、 app1 はMaterial Designのアイコンが必要ないのに取得しにいってしまうという無駄が生じてしまいます。

# もともとのbase.htmlからコピーして作る
cp templates/base.html templates/vuetify-base.html

<head> 内に次の1行を増やしておきましょう。

<link href='https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons' rel="stylesheet">

これでVuetifyが使える状態になっているはずです。でも見た目を何も変えていないので、 App.vue を下の内容にしちゃいましょう。

<template>
  <v-app id="inspire">
    <v-navigation-drawer v-model="drawer"
                         fixed
                         app>
      <v-list dense>
        <v-list-tile @click="onClick">
          <v-list-tile-action>
            <v-icon>home</v-icon>
          </v-list-tile-action>
          <v-list-tile-content>
            <v-list-tile-title>App1</v-list-tile-title>
          </v-list-tile-content>
        </v-list-tile>
      </v-list>
    </v-navigation-drawer>
    <v-toolbar color="indigo"
               dark
               fixed
               app>
      <v-toolbar-side-icon @click.stop="drawer = !drawer"></v-toolbar-side-icon>
      <v-toolbar-title>Application</v-toolbar-title>
    </v-toolbar>
    <v-content>
      <v-container fluid
                   fill-height>
        <v-layout justify-center
                  align-center>
          <v-flex text-xs-center>
            <v-tooltip left>
              <v-btn slot="activator"
                     :href="source"
                     icon
                     large
                     target="_blank">
                <v-icon large>code</v-icon>
              </v-btn>
              <span>Source</span>
            </v-tooltip>
          </v-flex>
        </v-layout>
      </v-container>
    </v-content>
    <v-footer color="indigo"
              app>
    </v-footer>
  </v-app>
</template>

<script>
export default {
  data: () => ({
    drawer: null
  }),
  props: {
    source: String
  },
  methods: {
    onClick() {
      window.location.href = '/app1';
    }
  }

};
</script>

vue.config.jsの設定

app2 を作ったところで、 vue.config.js でちゃんとビルドができるように設定しましょう。 ほとんど app1 と同じ内容になるだけです( templateが違うので注意! )。

...
    pages: {
        app1: {
            entry: 'src/app1/main.js',
            template: 'templates/base.html',
            filename: `../../resources/views/spa/app1.blade.php`,
        },
        app2: {
            entry: 'src/app2/main.js',
            template: 'templates/vuetify-base.html',
            filename: `../../resources/views/spa/app2.blade.php`,
        },
    },
...

フロントエンドのビルド

上までの手順でフロントエンド側は完了です。ビルドしてLaravel側から使えるようにしましょう!

# frontendディレクトリで作業
npm run build

Laravel連携

では、アセットの準備ができたので残りはLaravelが app2 を配信できるようにすることです。前編SpaController を作ってるので、これのメソッドを増やすことと、 Router をつなげればOKです。

SpaControllerは次のようにメソッドを増やします。

<?php
// app/Http/Controllers/SpaController.php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class SpaController extends Controller
{
    public function app1()
    {
        return view('spa/app1');
    }
    // ↓至ってシンプル
    public function app2()
    {
        return view('spa/app2');
    }
}

そして、ここに http://localhost/app2 のリクエストがフォワードされるように web.php を修正します。

<?php
// routes/web.php

Route::get('/app1{any}', 'SpaController@app1')->where('any', '(/?$|/.*)');
// app2用のルーティングを追加
Route::get('/app2{any}', 'SpaController@app2')->where('any', '(/?$|/.*)');

これで完成です!では ./vessel start していることを確認して http://localhost/app2 にアクセスしてみましょう。

f:id:kfukuyama:20190224212531g:plain

上図のような画面が見えたなら正常にMSPAが完成しています! app2 内ではVuetifyが動いていて、そこから app1 に遷移して、 app1前編 のときと同様に動作しているのがわかります。

まとめ

Laravel Mixをあえて使わずに、Vue CLIとLaravelを連携して、なるべくDX(Developer Experience)を損なわずに開発していく方法について前編・後編と見てきました。「でもLaravel Mixの方が簡単でしょ」って意見もたくさんあると思いますが、あえてフロントをLaravelに依存させない方法で攻めています。

  • バックエンドの言語が変わるかもしれない
  • フロントエンドのコードを別リポジトリで管理したくなるかもしれない
  • Vue CLIに超素晴らしい機能が追加されたけどLaravel Mixが対応するのが遅いかもしれない

など、理由はいくつか思い浮かびます。ただ、この手法をとった場合の「Hot Reload」が効かなくなる問題は結構デメリットとして大きいと思います。不可能なのかどうかという点も含めて引き続きDXを磨いていきたいと思います。

【前編】Vue CLI + Laravel によるMSPA (Multi-Single Page Application)

f:id:kfukuyama:20190225131307p:plain

大阪からこんにちは、福山健@kenfdev)です!

最近はLaravelとVueを触ることが多く、開発のしやすい仕組みづくりについて考えてます。Laravelは最初からフロントエンドの開発がしやすいように Laravel Mix の仕組みが用意されています。これを使うとさくっとVueやReactなどのSPAのパイプラインもLaravelに組み込むことができます。

とはいえ、

  • バックエンドにLaravelを使い続けるかわからない
  • フロントエンドエンジニアにLaravelを極力意識させたくない
  • Vue CLIをそのまま使いたい
  • Vue CLIの中でも pages 機能を使ってMSPA(Multi-Single Page Application)を作りたい

というような思いがある人も一定数いるのではないでしょうか?

上記をふまえて、VueCLIを使ってフロントエンドを作り、バックエンドをLaravelで作成しつつ複数のSPA(Multi-Single Page Application)を作る方法について紹介したいと思います。前編はMSPA(Multi-Single Page Application)ではなく単一のSPAを作るところまでにして、 後編 はMSPAについて詳しくみていきましょう。

長すぎて読む時間のない人へ

サンプルのリポジトリが以下にあるので、遊んでみてください。

github.com

前提

  • 読者はVue, Laravelの基本的な知識を持っている
  • 読者はdockerコマンドを一度くらいは叩いたことがある
  • 記事内のcomposerコマンドはDocker経由で行います(Docker使わない人は適宜読み替える必要があります)
  • Laravelのローカルでの開発は Vessel を使います
  • フロントエンド開発は(Dockerを使わず)ローカルで行います
  • Vue CLI 3 がインストールされているものとします

構成

前編の構成としては至ってシンプルです。

f:id:kfukuyama:20190222133015p:plain
前編の構成

Laravelの準備

Laravelをscaffold

まずはLaravelのプロジェクトを作成します。

docker run --rm -it \
    -v $(pwd):/opt \
    -w /opt shippingdocker/php-composer:latest \
    composer create-project laravel/laravel laravel-with-vue-cli

サーバーをVesselで立ち上げる

Laravelの開発には Vessel を使います。(Docker使って便利に開発しましょうってツールです。)

Vesselの用意

# ディレクトリ移動
cd laravel-with-vue-cli

# Vesselのインストール
docker run --rm -it \
    -v $(pwd):/opt \
    -w /opt shippingdocker/php-composer:latest \
    composer require shipping-docker/vessel

# vesselコマンドが使えるようにする
docker run --rm -it \
    -v $(pwd):/opt \
    -w /opt shippingdocker/php-composer:latest \
    php artisan vendor:publish --provider="Vessel\VesselServiceProvider"

# vesselの初期化
bash vessel init

Vessel開始

次のコマンドでvessel経由でDockerのLaravel環境が立ち上がります

./vessel start

起動が完了したら http://localhost にアクセスしてみましょう。Laravelのトップ画面が表示されるはずです。

laravel-top
Laravel

ダミーAPIの作成

Vueから使える Rest API を用意しておきたいので、ダミー(固定値しか返さない)で作ります。

f:id:kfukuyama:20190222133711p:plain
Rest API部分の作成

Controller作成
docker run --rm -it \
    -v $(pwd):/opt \
    -w /opt shippingdocker/php-composer:latest \
    php artisan make:controller API/EcosystemController --api

app/Http/Controllers/API/EcosystemController.php の内容は次のようにします。

<?php

class EcosystemController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return response()->json([
            'vue-router' => 'https://router.vuejs.org/',
            'vuex' => 'https://vuex.vuejs.org/',
            'vue-devtools' => 'https://github.com/vuejs/vue-devtools#vue-devtools',
            'vue-loader' => 'https://vue-loader.vuejs.org/',
            'awesome-vue' => 'https://github.com/vuejs/awesome-vue'
        ]);
    }
}

routes/api.php に以下を追加して GET /api/ecosystems にAPIが公開されるようにします。

<?php
Route::get('/ecosystems', 'API\EcosystemController@index');

では、Postmanなど使って GET http://localhost/api/ecosystems を実行してみましょう。次のレスポンスが返ってくるはずです。

{
    "vue-router": "https://router.vuejs.org/",
    "vuex": "https://vuex.vuejs.org/",
    "vue-devtools": "https://github.com/vuejs/vue-devtools#vue-devtools",
    "vue-loader": "https://vue-loader.vuejs.org/",
    "awesome-vue": "https://github.com/vuejs/awesome-vue"
}

これでバックエンド側はいったん完成です。次にフロントエンド側も作っていきましょう。

Vue CLI プロジェクトとLaravelの連携

フロントとしてはLaravel Mixを使うではなくて、Vue CLIで作ったプロジェクトをLaravelと連携します!この手順は主にVueのEvan You氏の yyx990803/laravel-vue-cli-3 のリポジトリが参考になります。

このセクションでやろうとしていることの概要は次のようなイメージです:

f:id:kfukuyama:20190222152017p:plain
VueCLIとLaravel

Laravel Mixのファイルを削除

Laravel Mixを使わないので不要なファイルを削除しましょう。

rm -rf package.json webpack.mix.js yarn.lock resources/assets

新しいVueプロジェクトの作成

f:id:kfukuyama:20190222145006p:plain
Vue CLI

Vue CLI をインストールしておいてください

Vue CLIを使って新しいプロジェクトを作ります。このとき、 features の選択肢で Router を追加しておきましょう(SPAのページ遷移もほしいので)。

$ vue create frontend

Vue CLI v3.4.0
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Linter
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a linter / formatter config: Basic
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In dedicated config files

Vueの Multi-Page Applicationの準備

前篇ではVueアプリは一つしか作りませんが、 後編 に向けて複数(MSPA)作っていくことを前提にしているので、 src 配下は1段下げて管理します。app1 とでもしておきましょう。( app2, app3 とVueアプリが増えていくと仮定)

下図のような構成になるようにします:

f:id:kfukuyama:20190222141543p:plain
srcの構成

# frontendディレクトリに移動
cd frontend

# 階層を1段下げるために今のsrcをapp1とする
mv src app1
# srcディレクトリを改めて作って
mkdir src
# その中に `app1` を入れる
mv app1 src/

階層が変わったので src/app1/views/Home.vue のコンポーネントを import しているところも忘れずに修正します。

...
// importのパスを修正
import HelloWorld from "@/app1/components/HelloWorld.vue";
...

Vue CLIには pages という便利な機能が備わっているので、簡単に複数のVueアプリを一括管理することができます。このあたりも詳細は 後編 で述べます。

frontend/vue.config.js というファイルを作成して、以下のような内容にしましょう。

module.exports = {
    // VueアプリをMSPA対応させる(↓の内容は次のセクションですぐ書き直します)
    pages: {
        app1: 'src/app1/main.js',
    },
};

いったんここまでとして、次に本格的にLaravel側との連携を行います。

VueアプリをLaravelで配信

では、今のところ個別のVueアプリとLaravelアプリがあるだけなので、2つがちゃんと連動するようにしましょう。具体的には:

  • npm run build したときにアセットがLaravel配下に作られる
  • Laravelから index.html にそうとうするものを配信する(アセットが配信できるように)

ようにします。

注意: 今から作る構成では、 npm run servewebpack devServerHot Code Replace (HCR) はできません。Laravelからアセットを配信したい(CSRFトークンなど、サーバーサイドで注入したいものがある)、ということとそうした場合にたぶんHCRはできないんじゃないかと思っているからです(違っていたら誰か教えてくださいー)。 なので、ソースを更新するとビルドは自動で走らせることができますが、ブラウザのリフレッシュは必要になります。

public/index.html の移動

frontend ディレクトリ内での作業になります

まず、 public/index.html は、LaravelのControllerに生成してもらうことになるので、 Vueとしての public 配下に置くのをやめます。代わりにVue CLIの pages では、アセット(js, cssなど)をテンプレートに自動注入する設定を pages.<アプリ名>.template で指定することができるため、ここに今までの index.html を、baseのテンプレートとして指定するようにします。

# ※frontendディレクトリ内で作業
# アセット注入用のテンプレートを入れておく場所を作成し
mkdir templates
# index.htmlをbase.htmlとして移動する
mv public/index.html templates/base.html

vue.config.jsの調整

では、 npm run build したときに意図したアセットが意図した場所に作られるために改めて vue.config.js を編集します。

module.exports = {
    // アセットはLaravelの `public` の `app` ディレクトリ配下に作成されるようにする.
    // appの中身はすべて自動生成されるものなので、バージョン管理からはずしておくことができます
    outputDir: '../public/app',

    // app配下にjs, cssなどが置かれるので、publicPathを調整する
    publicPath: '/app',

    // app1
    pages: {
        // app1のエントリポイント、テンプレート、出力先を調整
        app1: {
            entry: 'src/app1/main.js',
            template: 'templates/base.html',
            filename: `../../resources/views/spa/app1.blade.php`,
        },
    },
};

上の内容を行うことで、Laravelの resources/views/spa 配下に作られるようになります。 以下のディレクトリは自動生成したものしか作られないので、 .gitignore で無視しちゃいましょう(競合や最新版の生成忘れを防ぐために、CIで作らせておいたほうが安全)。

# .gitignore
/public/app
/resources/views/spa

LaravelにSpaControllerを作成

f:id:kfukuyama:20190222143424p:plain
SpaController

Vue CLI側で作ったアセットを配信する SpaController を作ります。

docker run --rm -it \
    -v $(pwd):/opt \
    -w /opt shippingdocker/php-composer:latest \
    php artisan make:controller SpaController

次の内容にして、Vue CLIが作ったアセットを配信できるようにしましょう。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class SpaController extends Controller
{
    public function app1()
    {
        return view('spa/app1');
    }
}

そして、 routes/web.php を調整して /app1/<このあとはなんでもOK> にアクセスがきた場合にベースの HTML を配信できるようにします。

<?php

Route::get('/app1{any}', 'SpaController@app1')->where('any', '(/?$|/.*)');

結合!

ここまでで概ね連携が完了しました。それではまずはVueのアセットを作成しましょう!

アセットの配信
# frontendディレクトリにいること
npm run build

完了したら http://localhost/app1 にアクセスしてみましょう。

f:id:kfukuyama:20190217221244p:plain
http://localhost/app1にアクセスした画面

上のような画面が出てきたら一応正解です。でもなんだかおかしいですね。 特にURLが http://localhost/app/1 となって、なぜか app/1 と分割されているところも怪しいです。

これは frontend/src/app1/router.js が悪さをしちゃってます。

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL, // ←ここ

vue.config.jspublicPath/app に指定しているので、VueRouterが base/app だと勘違いしちゃっています。ちょっと不格好ですが、 app1 配下で配信されることを前提としてハードコードで直しちゃいましょう。

// frontend/src/app1/router.js
export default new Router({
  mode: 'history',
  base: '/app1/', // ←修正

再度 npm run build して http://localhost/app1 にアクセスして、以下の画面になれば成功です!

f:id:kfukuyama:20190217221923p:plain

API結合

f:id:kfukuyama:20190222152504p:plain
Vue + Laravel API

前編の仕上げとして、最後にVueとLaravel間でAPIの結合をしましょう。VueアプリからLaravelへajaxリクエストを正常に行えるかチェックします。

修正した内容が継続的に build されるように次のコマンドを実行しておきます:

npm run build -- --watch

f:id:kfukuyama:20190222112615p:plain

上の画面の枠で囲った場所はハードコードされているのですが、ここを ajax でLaravelの /api/ecosystems からとってくるようにして、Vueで動的に表示できるようにしてみましょう!

ajaxリクエスト

ajaxのリクエストをするために定番の axios を入れます。

npm install axios

そして、 src/app1/views/Home.vue でAPIを叩けるようにします。次のように修正しましょう。

// src/app1/views/Home.vue の <script> 内
import HelloWorld from "@/app1/components/HelloWorld.vue";
import axios from "axios";

export default {
  name: "home",
  components: {
    HelloWorld
  },
  data() {
    return {
      ecosystems: [],
    }
  },
  async mounted() {
    const res = await axios.get("/api/ecosystems");
    // 子コンポーネントが扱いやすい形に整形しときます
    const ecosystems = Object.keys(res.data).map(key => {
      return {
        name: key,
        link: res.data[key]
      };
    });
    this.ecosystems = ecosystems;
  }
};

リフレッシュして開発者ツールのネットワークを確認してみます。

f:id:kfukuyama:20190222115057p:plain
ajaxが成功しているログ

/api/ecosystems に対してリクエストしていて、レスポンスもちゃんと返ってきています!Laravelへのリクエストも問題なくできてそうですね!

親コンポーネントから子コンポーネントへデータを渡す

最後に ajax でとってきた情報を HelloWorld コンポーネントに渡しましょう。

まずは propsecosystems を受け取れるようにしておいて、

// src/app1/components/HelloWorld.vueの<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String,
    // ecosystemsを受け取れるようにしときます
    ecosystems: Array,
  }
}

templatev-for を使って表示できるようにします:

<!-- src/app1/components/HelloWorld.vueの<template> -->
...
    <h3>Ecosystem</h3>
    <template v-if="ecosystems.length > 0">
      <ul>
        <li :key="item.name" v-for="item in ecosystems">
          <a :href="item.link" target="_blank" rel="noopener">{{ item.name }}</a>
        </li>
      </ul>
    </template>
    <template v-else>
      Loading...
    </template>
...

そして、親(Home.vue)から ecosystems を渡しましょう!

<!-- src/app1/views/Home.vueの<template> -->
  <div class="home">
    <img alt="Vue logo"
         src="../assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App" :ecosystems="ecosystems" />
  </div>

リフレッシュしてみて↓のようになっていればVueとLaravelの連携としては完成です!

f:id:kfukuyama:20190222120506g:plain
ajaxリクエストをしている様子

ここまででいったんVueとLaravelの結合は完了となります。Vue CLIとLaravelのそれぞれの依存度を最小にして、DX(Developer Experience)を保つ手法として使えるのではないでしょうか。(

今はVueのアプリが一つしかないので、 後編 は複数のVueアプリを作る方法について見ていきましょう!

【MySQL】グループごとに連番を振る

こんちには。 サーバーサイドがおっさんばかりなのはフロントエンドについていくだけの精神力が無くなったからだって話に戦慄しているエンジニアの長谷川です。
はじめてのエントリーなんで最近MySQLで試してみた内容をメモ程度に投稿してみます。

連番を振る

まずMySQLで連番を振りたい場合、ユーザー定義変数を使ってインクリメントさせてできます。
元データ

Ships
id name
2001 飛竜
2002 蒼龍
2003 加賀
2004 大鳳

これに対してこうすると

SELECT id, name, @n:=@n+1 AS no
FROM Ships, (SELECT @n:=0) m;

結果

id name no
2001 飛竜 1
2002 蒼龍 2
2003 加賀 3
2004 大鳳 4

元のデータから引き続いて連番を振る

元のテーブル参照して一旦仮テーブルに連番振りたいときは結合します。

Ships
id name no
1001 三笠 1
1002 長門 2
1003 陸奥 3
ShipsTemp
id name
2001 飛竜
2002 蒼龍
2003 加賀
2004 大鳳

Shipsが元テーブルでそのnoの最大値3から連番を振ります。

SELECT id, name, @n:=@n+1 AS no
FROM ShipsTemp, (SELECT @n:=max(no) FROM Ships) m;

結果

id name no
2001 飛竜 4
2002 蒼龍 5
2003 加賀 6
2004 大鳳 7

グループごとに連番を振る

ここまではよかったんですが、今回はデータ全体で連番ではなくグループごとに連番を振りたかったんですね。

Ships
id name no class
1001 金剛 1 戦艦
1002 比叡 2 戦艦
1003 榛名 3 戦艦
1004 霧島 4 戦艦
1005 筑摩 1 重巡洋艦
1006 利根 2 重巡洋艦
1007 高雄 3 重巡洋艦
1008 長良 1 軽巡洋艦
1009 阿武隈 2 軽巡洋艦
1010 葛城 1 航空母艦
ShipsTemp
id name class
2001 大和 戦艦
2002 飛竜 航空母艦
2003 足柄 重巡洋艦
2004 鈴谷 軽巡洋艦
2005 武蔵 戦艦
2006 蒼龍 航空母艦
2007 妙高 重巡洋艦
2008 熊野 軽巡洋艦
2009 加賀 航空母艦
2010 那智 重巡洋艦
2011 大鳳 航空母艦

これに対して結果が以下のようになるクエリーがないものか

id name no class
2001 大和 5 戦艦
2005 武蔵 6 戦艦
2002 飛竜 2 航空母艦
2006 蒼龍 3 航空母艦
2009 加賀 4 航空母艦
2011 大鳳 5 航空母艦
2004 鈴谷 3 軽巡洋艦
2008 熊野 4 軽巡洋艦
2003 足柄 4 重巡洋艦
2007 妙高 5 重巡洋艦
2010 那智 6 重巡洋艦

これはさすがにSQLじゃなくて一旦SELECTで取ってきてプログラムでループで回すかストアドプロシージャ使わないと無理だろうとなと思ったんですが調べてみました。

とりあえずGROUP BY

無理でしょうけどとりあえずGROUP BYして連結してみる。

SELECT id, t.name, @n:=@n+1 AS no, t.class
FROM ShipsTemp t
  INNER JOIN (
    SELECT @n:=max(no), class
    FROM Ships
    GROUP BY class
  ) m ON m.class = t.class
ORDER BY t.class, id;

結果

id name no class
2001 大和 4 戦艦
2005 武蔵 5 戦艦
2002 飛竜 6 航空母艦
2006 蒼龍 7 航空母艦
2009 加賀 8 航空母艦
2011 大鳳 9 航空母艦
2004 鈴谷 10 軽巡洋艦
2008 熊野 11 軽巡洋艦
2003 足柄 12 重巡洋艦
2007 妙高 13 重巡洋艦
2010 那智 14 重巡洋艦

どうやら最初に3はセットされるけどその後はただインクリメントされるようです。

グループ別最大値を取得してカウンターの値を足す

最大値とカウンターを分ければいいんじゃね?

SELECT id, t.name, max_no+@n AS no, t.class, @n:=@n+1
FROM ShipsTemp t
  INNER JOIN (
    SELECT max(no) AS max_no, class
    FROM Ships
    GROUP BY class
  ) m ON m.class = t.class,
  (SELECT @n:=1) n
ORDER BY t.class, id;

結果

id name no class @n:=@n+1
2001 大和 5 戦艦 2
2005 武蔵 6 戦艦 3
2002 飛竜 4 航空母艦 4
2006 蒼龍 5 航空母艦 5
2009 加賀 6 航空母艦 6
2011 大鳳 7 航空母艦 7
2004 鈴谷 9 軽巡洋艦 8
2008 熊野 10 軽巡洋艦 9
2003 足柄 12 重巡洋艦 10
2007 妙高 13 重巡洋艦 11
2010 那智 14 重巡洋艦 12

カテゴリーごとの最大値は反映してるけど、カウンターは上昇し続けてます。
それはそうだ、変数はいつも一つ!
重複がなけりゃいいーっていう問題ならこれでも……。

グループが変わったところでカウンターをリセット

ならば変数を2つにしてカウンターが変わったらカウンターをリセットすればいいんじゃね?

SELECT
  id,
  t.name,
  @c AS old_class,
  CASE WHEN @c<>t.class THEN @n:=1 ELSE @n:=@n+1 END AS counter,
  max_no+@n AS new_no,
  @c:=t.class AS new_class
FROM ShipsTemp t
  INNER JOIN (
    SELECT max(no) AS max_no, class
    FROM Ships
    GROUP BY class
  ) m ON m.class = t.class,
  (SELECT @n:=1, @c:='') c
ORDER BY t.class, id;

結果

id name old_class counter new_no new_class
2001 大和 1 5 戦艦
2005 武蔵 戦艦 2 6 戦艦
2002 飛竜 戦艦 1 2 航空母艦
2006 蒼龍 航空母艦 2 3 航空母艦
2009 加賀 航空母艦 3 4 航空母艦
2011 大鳳 航空母艦 4 5 航空母艦
2004 鈴谷 航空母艦 1 3 軽巡洋艦
2008 熊野 軽巡洋艦 2 4 軽巡洋艦
2003 足柄 軽巡洋艦 1 4 重巡洋艦
2007 妙高 重巡洋艦 2 5 重巡洋艦
2010 那智 重巡洋艦 3 6 重巡洋艦

いい具合にclassが変わったところでカウンターがリセットされました。

安全のために並び替え

でもこれ ORDER BY が効いてグループ順に処理してるっていう確証がないと破綻しちゃうよね。
SELECTORDER BYより先に評価されるって言うから、今回たまたまうまく行っただけで場合によってはグループ順に並ばないかもしれません。
ならば、先に並び替えたテーブルを結合すれば確実にグループ順に処理されるでしょう。

SELECT
  t2.id,
  t2.name,
  @c AS old_class,
  CASE WHEN @c<>t2.class THEN @n:=1 ELSE @n:=@n+1 END AS counter,
  max_no+@n AS new_no,
  @c:=t2.class AS new_class
FROM
  (SELECT * FROM ShipsTemp ORDER BY class, id) t2
  INNER JOIN (
    SELECT id, max_no
    FROM ShipsTemp t
      INNER JOIN (
        SELECT max(no) AS max_no, class
        FROM Ships GROUP BY class
      ) m ON m.class = t.class
    ORDER BY t.class, id
  ) m2 ON m2.id=t2.id,
  (SELECT @n:=1, @c:='') c
ORDER BY class, t2.id;

結果は同じです。

結論

試した限りではグループごとに連番振ることができました。
しかし、これで確実にグループ順に処理されるという確証は欲しいところです。

とあるサイダスの開発チーム  第1弾

はじめまして

エンジニアをやっている 勇大(id:yudy0115)と言います。 2018 年春から転勤で沖縄にやってきました。 数年で東京に戻る予定でしたが、あまりにも住みやすく、移住を決意したしだいです。

第1弾として・・・

まず第1弾として、プロローグ的な意味も含めまして、どうして書いていこうかと思ったのか、 どういったことを書いていくのか、を紹介したいと思います。

私のいるチームはスクラムで、2019 年からは基本ずっとモブプロでやっています。

まだまだ勉強中で、日々試行錯誤しています。

普段の取り組みや、悩み、うまくいったことなど、その過程を記事にできたらおもしろいなと思い、記事にしてみました。

サイダスの開発風景と言いますか、雰囲気を紹介できたらいいなと思っています。

※ 普段はこんな感じで常に zoom を使ってリモートで開発してます。念の為モザイクをかけたらほぼモザイクという・・・

f:id:yudy0115:20190219164726j:plain
zoom でリモート開発

さっそく、昨年末でのふりかえり内容

ほんと唐突ではありますが、昨年末にその時のチーム内で行ったふりかえりの内容が以下の通りです。

  • 納期と時間の意識がついた。
  • あまりいいものができていないと感じた(もともと予定してたベータ版のリリースも遅れた)
  • こなれたら爆速になるかと思ったけど、意外とならなくて難しかった。
  • もっとトライ&エラーが必要かも。
  • AWS のサービスをたくさん触れた。
  • スクラムをやる前と後であまり違いを感じなかった。
  • スクラムの良さを活かしきれていない。
  • 作業を分担することが多く、結局タスクの属人化になってしまった。
  • スプリントごとのふりかえりで KPT をやっていたが、個人の感想を話すくらいになってしまっていた。
  • スクラムマスター的な存在が必要(※そもそもマスターを決めずに進めていたという・・・)
  • プログラミングに強くない人でも入りやすくするための施策が必要(※内定者もいるしね)

2018 年を通していろいろ思ったこと、感じたこと

正直言いますともともとは、1人でモクモクと、あれやこれやと試しながら開発していくことが好きでした。 要望されているものが実現できればやったった感はありますし。

しかし、去年を通して何かしら感じることがあったのでしょう。 こういう風に思うようになりました。(1人で聖なる夜を過ごしている時に)

  • いいチームってどんなんだろう?
  • チームで動くってどういうことだろう?
  • 属人化はよくないのは分かってる。じゃあそこから脱却するには・・・?
  • 1人でやるのに飽きた
  • やったことを共有する、としても1日経てば忘れちゃうよね(少なくとも自分はそう!)
  • いっしょに体験することが重要なのでは?

などなど。

そして、そもそもアジャイル開発って?スクラムって?というところからまずは勉強してみようと思いました。 本を読むことは苦手中の苦手ではありますが、何冊か読み(ちゃんと読破したものはありませんが)、 2019 年から始まる新チームで進めていくにあたって、まずはどういう取り組みをしていったらいいかな、というのを考えてメンバーに提案しました。

※ 参考にした本など、後ほど紹介させていただきます!

2019 年始動!

といっても今はもう2月半ば過ぎちゃっていますが。。。

どういうチーム構成かと言いますと、3人です。(少なっ!と感じる方が多いかも?)

私と1人は沖縄。もう1人は東京。 いわゆるリモートメンバーも含めたチーム構成になっています。 といっても、去年3ヶ月間は沖縄でみんな集まってやっていたので気心知れるメンバーです。

まず 2018 年の経験を活かすためにも以下のことを取り組んでいこうとなりました。

ワーキングアグリーメントをしっかり書いてみよう

狙いとしては、去年の反省の中で、ふりかえりがちゃんと KPT になっていない、というものがありました。 それを改善する施策として、ワーキングアグリーメントをちゃんと明文化し、それを軸にしてよかったことや、よくなかったことなどを出していってみよう、となりました。

ワーキングアグリーメントは以下の通りになりました。

  • スクラム
    • スプリント
      • 開始曜日:水曜日
      • 期間:1週間
    • デイリー MTG 10:15 〜 10:30
    • ストーリポイントをつけよう:10:30 〜 10:45
    • モブプロのふりかえり:17:00 〜 17:15
    • スプリントレビュー:水曜日の 11:00 〜
    • スプリントのふりかえり:水曜日、ひと段落したら始める
  • ドキュメント管理などのツール
    • プロジェクト管理:Asana (Trello のようなボードでタスク管理)
    • KPT:Asana
      • 去年は毎回 Slack にメモ程度に書いていただけなので、ちゃんと見える化するため、タスクのボードとは別ボードで管理
    • ちょっとした文書は Slack のポストを利用しよう
    • 資料となるものは Box に
  • その他諸々
    • 仕事のスタイル
      • 基本モブプロで行う(設計、実装など)
      • モブプロは VSCode の LiveShare を利用する
      • ポモドーロ形式でドライバーを交代していく
        • ポモドーロは、25 分作業、5 分休憩、のリズムで行っていく
        • 10:45 〜 25 分 × 3 回
        • 13:30 〜 25 分 × 9 回、できたらいいね
      • プルリク不要
      • 割り込みタスクが発生したらすぐに伝えてね
    • 実装は、テストも含めて完了状態とする
    • OKR
      • 個人でというよりも、むしろ支援出来るものは支援していこう
      • ※ 今年から弊社技術部全体で OKR を設定するようになりました
    • 飲み会
      • 早くあがって飲み行きたい日は早めに申請すること!
      • ※ 特に私用の項目です・・・
    • タイムボックスを守るためにタイマーを使おう
    • A or B で迷った時、工数が少なく済む方でトライしてみよう
    • エラー報告の時
      • キャプチャを添付すること
      • エラーが起きた時の状態(ログインIDなど)を記載すること

全て守られているかと言うと決してそうではありません。

最近はこれらのメンテナンスを怠っています・・・。課題ですね。

この約1ヶ月半で、アグリーメントに追加した方がいいもの(暗黙的に行っていること)や、現状と合わないものがあるので、チーム内で整理し、また後日紹介したいと思います。

ふりかえりを可視化しよう

上のワーキングアグリーメント内にもありましたが、ふりかえりの内容を Slack に記載して埋もれさすのではなく、Asana を利用していつでも見れるように、ふりかえりしやすいようにしたいと思いました。

2019/02/13 時点でのボードの様子がこちらです。

f:id:yudy0115:20190220003701p:plain
KPT

今は縦の列が5つありますが、最初は単純に Keep, Problem, Try の3つだけでした。

何回かスプリントをまわしていくうちに、

  • 「この Keep はもう意識して出来てるね。このカードどうしよう、消しちゃう?」
  • 「いや〜、勿体無いでしょ〜。出来るようになったことも溜まっていけば何だか嬉しいし。」
  • 「じゃあ、もう1列足して残しとく?」

だいたいこのような会話が出て、「習慣化した Keep」という列を足すことになりました。

1つの例ではありますが、ちっちゃいことでもまずはやって、後で問題、課題が出てきたら改善していこうの精神でやっています。

※ 「習慣化した Keep」はこちらの記事を参考にさせていただきました。

qiita.com

こちらの記事のように Coggle というサービスもしくは XMind のようなマインドマップを描けるツールを使おうか?という話しも出たのですが、まずは使い慣れている Asana でやってみようとなりました。

基本すべてにおいてモブプロでやってみよう

まず、アプリ開発経験を長くやっているのは私1人、ということ。 今後入ってくるであろう新卒のメンバー等の受け入れにも役立てられるのでは?という狙いもあります。

タスクの属人化から脱却したい、ということ。 これは少なくとも、○○の機能のバックエンドなら誰々さんに聞けばいいよね、から、○○の機能であればあのチームに聞けばいいよね、といった具合に、 個人からチームの属人化(属人化って言うの??)には脱却できるかも、という狙いがあります。

参考にした本など

アジャイルソフトウェア開発の奥義 第2版 オブジェクト指向開発の神髄と匠の技

アジャイルソフトウェア開発の奥義 第2版 オブジェクト指向開発の神髄と匠の技

  • 作者: ロバート・C・マーチン,Robert C. Martin,瀬谷啓介
  • 出版社/メーカー: SBクリエイティブ
  • 発売日: 2008/07/01
  • メディア: 単行本
  • 購入: 18人 クリック: 586回
  • この商品を含むブログ (64件) を見る

分厚いです(712 P)!お値段もなかなかです!会社にあってよかった・・・ なかなかの量なので、「年末年始休暇で読破してやるぜ!」の意気込みはすぐに捨て去りました。

  • 第1部:アジャイル開発
  • 第2部:アジャイル設計

を主に参考にしました。

エッセンシャル スクラム: アジャイル開発に関わるすべての人のための完全攻略ガイド (Object Oriented Selection)

エッセンシャル スクラム: アジャイル開発に関わるすべての人のための完全攻略ガイド (Object Oriented Selection)

スクラムにはたくさん聞き慣れないカタカナ用語が出てきますよね?(スプリントレトロスペクティブだなんてきっと誰も最初は噛むはず・・・!)

そんなスクラムにおけるたくさんのイベント(プランニングなど)で、どういったことをやったら良いのか詳細に書かれています。

なのでスプリントプランニングではどういったことを意識してやったら良いんだろう?って思った時に読んだりして、辞書的に読んだりしました。

WEB+DB PRESS Vol.102

WEB+DB PRESS Vol.102

そもそもモブプロのイメージは、1人がコーディングして、周りがチェックして、みたいなイメージがありました。(ちょっとした監視的な・・・)

でもそうじゃないんだよ、楽しくやれるんだよ、ということが書かれていまして、楽しくモブプロをやっていくにはどうやっていったら良いだろうか、というヒントになりました。

次回予告

第1弾ということもあり、まずは簡単に書かせていただきました。 次回は、実際に取り組んだふりかえりの方法や、モブプロをどう進めたかなどを書いていきたいと思います。

どうもありがとうございました。