CYDAS Developer's Blog

サイダス技術者ブログ

【前編】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弾ということもあり、まずは簡単に書かせていただきました。 次回は、実際に取り組んだふりかえりの方法や、モブプロをどう進めたかなどを書いていきたいと思います。

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

DeepRacerを走らせる!ー テストドライブ編 ー

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

前回に引き続きまして、今回はいよいよDeepRacerを走らせます!

DeepRacerのセットアップがまだの方はこちら〜
tech.cydas.com

Step.3 テストドライブ

パワーバンクに接続する

部品の中からパワーバンクの接続ケーブルを取り出して
パワーバンクのUSB-Cポートと車体のUSB-Cポートをつなぎます。
つなぐとこんな感じ↓
f:id:aym413:20190127135402j:plain

パワーバンクの電源を入れる

パワーバンクの電源ボタンを押します。
f:id:aym413:20190127140236j:plain

車体の電源を入れる

車体横の電源ボタンを押します。
f:id:aym413:20190127141921j:plain

しばらくすると電源ボタンのマークが青色に点灯し、Wi-Fiに接続し始めます。

続いて、LEDランプも点灯状態になると、Wi-Fiの接続が完了されました!
f:id:aym413:20190127143021j:plain

車体のスイッチをオンにする

フロントタイヤの左側(車体の電源ボタンがある側)にスイッチがありますので
そのスイッチをONにします。
f:id:aym413:20190128214850j:plain

パカっと開けたココ↓
f:id:aym413:20190128214334j:plain

そのとき、ピピッ、ピーー
短いビープ音2回・長いビープ音が1回鳴ればOKです!

もしビープ音が鳴らなかった場合は、カーバッテリーの充電がないかもしれません。
こちらの手順を参考にカーバッテリーを充電してください↓
docs.aws.amazon.com

カバーをセットする

車体にカバーをかぶせ、ピンで4箇所固定します。
さあ、DeepRacerの準備は整いましたよ〜〜!
f:id:aym413:20190127145804j:plain

DeepRacerを走らせる

セットアップ編でも使用したdevice-status.txtファイル内に
記載されているIPアドレスをブラウザで開きます。

ログイン画面が表示されるので、Passwordを入力して
「Access vehicle」をクリックします。
f:id:aym413:20190125220347p:plain

Passwordは車体の裏側に記載してあります。
f:id:aym413:20190127145315j:plain

f:id:aym413:20190127145419j:plain

アクセスしたら、「Driving Mode」をManualにします。
f:id:aym413:20190128230249j:plain

操作方法は「Click or touch to drive」のところでクリックして
中心円を上下左右に動かすとDeepRacerが走ります。
画面上で操作するのと、DeepRacerが走り出すのに若干のタイムラグがあります。
操作は前と後ろが逆なので慣れないと難しい・・・
f:id:aym413:20190128233756j:plain

私が実際にDeepRacerを走らせてみた動画がこちらです!↓

走らせるスピードの調整がなかなか難しくて、思ったように走ってくれず・・
日本に来るのが待ち遠しいですね!

では!

DeepRacerを走らせる!ー セットアップ編ー

こんにちは!エンジニアのこぶかた@kobuuukata)です!

昨年、re:Invent 2018に参加してきました!
その中でDeepRacerが発表されました!

DeepRacerについての説明はこちら↓ aws.amazon.com

現地でワークショップに参加したところ・・・

DeepRacerゲットしました〜!

ジャジャーーーン♪
f:id:aym413:20190118211215j:plain

早速現地でセットアップからテストドライブまで試してみました!

セットアップの手順はこちらにあります↓
https://d1.awsstatic.com/deepracer/AWS-DeepRacer-Getting-Started-Guide.pdf

Step.1 車を組み立てる

車体についているピンを外す(4箇所)

DeepRacerを箱から取り出して、ピンを外します。
f:id:aym413:20190127131953j:plain

パカっとするとこんな感じ
f:id:aym413:20190118223249j:plain

カーバッテリーのケーブルを接続

部品の中からカーバッテリーを取り出して、
車体についている赤と黒のケーブルとカーバッテリーのケーブルを繋げます。

ここで注意!

ケーブルを繋げる際は、

同じ色同士を接続するようにします!

f:id:aym413:20190125231640j:plain

カーバッテリーを車体に取り付け

車体のベルトを外してカーバッテリーを取り付けます。
f:id:aym413:20190125231950j:plain

車体をピンで固定(4箇所)

ケーブルを車体の中に入れ、ピンで固定します。
f:id:aym413:20190124121103j:plain

パワーバンクの取り付け

部品の中からパワーバンクを取り出して
電源ボタンがある方を車体の後方の向きになるようセットします。

ん?電源ボタン??どこどこ???

しばらく探していると、

あーー!シール貼ってあるじゃん!!
f:id:aym413:20190124121210j:plain

全然気づかなかった、、
剥がしてみると、電源ボタン出現!!

改めてパワーバンクを車体に取り付けます。
ここで注意!

ここではまだパワーバンクの電源は入れないでください。

f:id:aym413:20190127132347j:plain

パワーバンクを充電する

DeepRacerを走らせる際、パワーバンクが充電されている必要がありますので
ここでパワーバンクを充電しておきましょう。
パワーバンクのUSB-Cポートに電源ケーブルを挿して、コンセントにつなぎます。
f:id:aym413:20190128132212j:plain

Step.2 接続する

コンセントに接続する

車体にあるUSB-Cポートに電源ケーブルを挿して、コンセントにつなぎます。

車体の電源を入れる

車体横の電源ボタンを押してください。
パワーバンクの電源ではなく、車体の電源です!
f:id:aym413:20190127142251j:plain

しばらくすると電源ボタンのマークが青色に点灯します。
ピカーーーン!

Wi-Fiを設定する

USBメモリをPCに挿しておきます。

下記からWi-Fi設定用のファイルをダウンロードします。
https://d1.awsstatic.com/deepracer/wifi-creds.txt

ダウンロードしてきたら、ファイルを開いて
Wi-FiのSSIDPasswordを入力します。
f:id:aym413:20190124123237p:plain

入力したらUSBメモリに保存します。
ファイル名は変えずwifi-creds.txtのまま保存してください。
f:id:aym413:20190124123654p:plain

PCからUSBメモリを外し、車体のUSBポートに挿します。
すごく挿しづらいところにある・・
車体の後方、パワーバンクの後ろです。
f:id:aym413:20190125232237j:plain

f:id:aym413:20190124151525j:plain

しばらくすると、Wi-FiのLEDランプが青色に点灯します。
電源ランプとLEDランプが両方点灯していればオッケーです!
f:id:aym413:20190125125712j:plain

もし、LEDランプが赤に点灯していたら
Wi-FiのSSIDかPasswordが間違っていますので
USBメモリを外して再度確認してください!

ソフトウェアのアップデート

Wi-FiのLEDランプが青色になっているのが確認できたら
一度USBメモリを車体から外して、再度USBメモリをPCに挿します。

USBメモリ内にdevice-status.txtという
ファイルが作られているかと思いますのでそちらを開きます。

ファイルの中にIPアドレスが記載されているのでコピーします。
f:id:aym413:20190125130551p:plain

コピーしたIPアドレスをブラウザで開きます。
こんな警告画面出ますが、気にせず!
f:id:aym413:20190125131045p:plain

「詳細設定」 > 「XXXXXにアクセスする(安全ではありません)」をクリックします。
f:id:aym413:20190125131407p:plain

DeepRacerのソフトウェアをアップデートします。
電源がコンセントにささっていることを確認して
「Yes, update vehicle software」をクリックします。
f:id:aym413:20190125214952p:plain

クリック後、アップデートが始まります。
アップデート中はこんな画面↓
f:id:aym413:20190125215504p:plain

3分ほど待つと・・・

車体のLEDランプが点滅→点灯状態に変わったらアップデート完了です!
f:id:aym413:20190127134334j:plain

車体のUSB-Cポートから電源を外します。
車体のLEDランプが消えますが問題ありません!

DeepRacerのセットアップはこれで終わりです!

次はいよいよDeepRacerを走らせますよ〜〜!

Nuxt.jsのinjectを使ってDIする

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

先日「v-kansai Vue.js/Nuxt.js meetup #2」で「Nuxt.jsのinjectでインジェクトしてみる話」という内容で登壇させていただきました。その内容について記事にも残しておこうと思います。

スライドに関しては以下で公開しています。

speakerdeck.com

前提知識

  • 基本的なNuxt.jsの機能
  • Vuexをなんとなく知っている
  • TypeScriptがなんとなく読める

Nuxt.jsの inject

皆さん、Nuxt.jsには inject という機能があるのをご存知でしょうか?意外と知られていないんじゃないかと思うのですが、公式ドキュメントの「統合された注入」にも記載されているNuxt.jsのプラグインの機能です。この inject を使うことで、Nuxt.jsのアプリケーションで、いろいろな場所から共通で利用したい関数や値を(グローバル変数にすることなく)呼び出すことができます。「グローバル変数や、外部モジュールから import で読み込んじゃえばいいんじゃないの?」と思われるかもしれませんが、自分の興味のあるコンテキスト内(例えばある関数Aの中身)に、差し替え可能な状態で関数や値が用意されていると、何かと便利です(特にtestabilityの観点で)。このトピックと関連が深いのが「Dependency Injection(依存性の注入)」です。

Dependency Injection?

Dependency Injectionが何なのか。何がうれしいのか。と思われる方もいると思いますが、それを話し始めるとこの記事が(ただでさえ長いのに)すごく長くなってしまうので、ぜひ「dependency injection 何がうれしい」と ググってみて ください。先人たちの素晴らしい記事が何個も出てくると思います。

Demoアプリ

inject-demo-app
injectを使ったdemoアプリ

inject を実際に小さなアプリで使ったものを上図のようなアーキテクチャで公開しています。

github.com

このアプリはVue.jsとNuxt.jsのGitHubのスター数をGitHubAPIから取得して合算したものを画面に表示するという至ってシンプルなものです。大きな特徴としては「状態管理パターンとはなんですか?」で紹介されているVuexの登場人物に加えて、「Gateway」という存在を追加しています。これはActionsから直接 axios などを使ってリクエストを投げるのではなく、Gatewayさんに依頼して(ワンクッション置いて)リクエストを投げるという意味です。

プラグインの書き方

プラグインの書き方は、次のような記述を plugins/dependencies.ts というファイル(TypeScriptを使用しています)に書きます。

export default (context, inject) => {
  const environment = process.env.environment || 'development';

  let gitGateway: IGitGateway;
  if (environment === 'offline') {
    gitGateway = new FakeGitGateway();
  } else {
    gitGateway = new GitHubGateway(axios);
  }

  const deps: Dependencies = {
    gitGateway,
  };

  inject('deps', deps);
};

inject('deps', deps) としているところがミソで、これでアプリケーションの様々な場所から $deps として呼べるようになります。

$deps の呼び方

上の設定で用意した $deps は大きく3箇所から呼ぶことができます。

  • Nuxt.jsの context
  • Vueのインスタンス
  • VuexのActionの中(store自身)

Nuxt.jsの context

Nuxt.jsの context は様々な場所で参照できますが、例えば pagesfetch の第一引数にも渡されてきます。次のようにアクセスできます:

fetch(context) {
  // context.app.$deps.gitGateway
  // ...
},

Vueのインスタンス

Vueのインスタンスからは、例えば methods 内から呼び出すことができます。インスタンス自身なので this から参照できます。

methods: {
  onLoad() {
    // this.$deps.gitGateway
    // ...
  },
},

VuexのActionの中

今回のパターンで使いたい、Actionから参照する方法です。 store 自身にも $deps が注入されているので、次のように参照できます。

export const actions = {
  async [actionTypes.FETCH_STARS]({ commit }) {
    // this.$deps.gitGateway
    // ...
  },
};

このように、様々な場所から $deps が呼び出せるようになります。

何がうれしいのか?

では、 $deps が呼び出せるようになって何がうれしいかというと、ソフトウェアアーキテクチャを考えると色々と例があると思いますが、以下の2点を紹介したいと思います。

  • Vuex内が要件の変化に強くなる
  • 開発モードみたいな機能を追加できる

Vuex内が要件の変化に強くなる

今回の要件は「リポジトリのスター数をとってきて表示する」というものだったとします。リポジトリがどこにあるかもわからなければ、その数もわからなかったとしましょう。「まだわからないので開発もしない」わけではなく、詳細はともかく、要件は決まっているところから着手していくことができます。

「どうやってスター数をとってくるか」はGatewayさんにおまかせします。それ以上の詳細は今は気にしません。図で表すと次の部分は作ることができるということです。

TypeScriptで表現した場合、Gatewayさんは IGitGateway として例えば次のように interface を定義することができます。

export interface IGitStarsResult {
  count: number;
}

export interface IGitGateway {
  fetchStars(): Promise<IGitStarsResult>; // 「スター数とってきてー」メソッド
}

ということで、VuexのActionは次のような実装で書くことができます。

export const actions: ActionTree<State, State> = {
  async [actionTypes.FETCH_STARS]({ commit }) {
    const { gitGateway } = this.$deps as Dependencies; // $depsからgitGatewayを参照

    commit(mutationTypes.SET_LOADING, true);
    const { count } = await gitGateway.fetchStars(); // スター数とってきてー
    commit(mutationTypes.SET_LOADING, false);

    commit(mutationTypes.SET_COUNT, count); // スター数をストアに保存!
  },
};

TypeScriptを使っていて as を使うのは極力避けたいのですが、今の所 $deps に型情報を簡単に反映させるには as Dependencies が良いと思って使っちゃっています。こうすることで次のように補完も効いてちょっと安心して開発ができます。

code-completion
Action内でのコード補完

では、「どうやってスターをとってくるか」が不明な状態で、実際の gitGateway はどう実装すればいいのでしょう?という疑問がわいてくると思うのですが、わからないうちは次のようにスタブを作っちゃいましょう。 IGitGateway を満たしつつ、1.5秒後に {count: 2} を返す FakeGitGateway を次のように実装できます:

export class FakeGitGateway implements IGitGateway {
  fetchStars(): Promise<IGitStarsResult> {
    return new Promise(resolve => {
      setTimeout(() => {
        resolve({ count: 2 });
      }, 1500);
    });
  }
}

これを plugins/dependencies.ts で差し込むことで、Action内で参照する gitGatewayFakeGitGateway のインスタンスになります。

// plugins/dependencies.ts
export default (context, inject) => {
  const gitGateway = new FakeGitGateway();
  const deps: Dependencies = {
    gitGateway,
  };

  inject('deps', deps);
};

以上で、スター数は2で固定となってしまいますが、Vuexの中身の実装は先行して作り切っちゃうことができます。

後になって「GitHubからスター数をとってきて」という詳細がきまったときに、 GitHubGateway を実装して、 FakeGitGateway と差し替えることで、Vuex内は何も変えずに最終型を作り上げることができます。

// plugins/dependencies.ts
export default (context, inject) => {
  // const gitGateway = new FakeGitGateway();
  const gitGateway = new GitHubGateway();
  const deps: Dependencies = {
    gitGateway,
  };

  inject('deps', deps);
};

GitLabからスター数をとってきたい場合であれば GitLabGateway を実装すればいいですし、GitHubとGitLab両方から取得する場合も、そのようなGatewayを作って差し込めばOKです。

要件の変化に強いVuex

アーキテクチャ寄りな話になってしまいましたが、 inject を使ったメリットの一つとして紹介しました。

開発モードみたいな機能を追加できる

次に、上の差し替えに関連するのですが、Nuxt.jsの 環境変数 と組み合わせることで、 npm run offline のように、インターネットに通信しにいかないモードを作ることもできます。次のように plugins/dependencies.ts を実装することで、実行時のGateway切り替えが可能になります。

// plugins/dependencies.ts
export default (context, inject) => {
  const environment = process.env.environment || 'development';

  let gitGateway: IGitGateway;
  if (environment === 'offline') {
    // offline時はスタブを使う
    gitGateway = new FakeGitGateway();
  } else {
    // それ以外は本物の実装を使う
    gitGateway = new GitHubGateway(axios);
  }

  const deps: Dependencies = {
    gitGateway,
  };

  inject('deps', deps);
};

こういうモードを分けることで、Integration Test時にGatewayを差し替えたり、あるいは「API側がバグっていてフロントの開発ができなくなる」というような状況を回避することもできたりするので、意外とDX(Developer Experience)の向上に貢献するのではと思います。

気になった点

「うれしかった」と感じた点はいったんここまでとして、やってみて「気になった」点について2点ほど共有したいと思います。

Lazy Loadはされるのか?

中規模くらいなアプリケーションであればさほど気にしなくてもいいのかもしれませんが、 現時点でプラグインのLazy LoadというものはNuxt.jsには無いという認識です。なので、 plugins/dependencies.ts が肥大化すると、そこに関連するコードはすべてメインのbundleに含まれることになると思います。アプリケーションが大規模になっていくと、bundleのサイズも無視できなくなってくるので、結構致命的な問題じゃないかな、と思っています。ここらへんは今後Nuxt.js的に改善されていくかもしれないので要チェックです。

$deps はVuexのStoreで参照できれば十分では?

Vuexを使うアプリであれば、ビジネスロジックに関わる部分はAction内にほとんど収まるはずなので、StoreのAction内でだけ this.$deps が参照できたら十分では?と思いました。 injectのコード を見ても、 store$deps を代入してるだけなので、なんら難しいことはしてなさそうです。Vuexのモジュールは 動的に登録 することができるので、Lazy Loadしながら、 store の中に注入していけるのではと思います。

まとめ

最後に inject を使ってのまとめです。

  • 中規模なアプリ(明確な線引はできませんが、パフォーマンスとの兼ね合いでしょうか)であれば inject を使ってのDIは便利に使えそう
  • ↑に関連して、プラグインもLazy Loadできるような仕組みがNuxt.jsにできれば、 plugins/dependencies.ts も複数ファイルに分割して大規模なアプリでも一つのファイルが肥大化しちゃう問題は回避できそう
  • 実は store でしか inject されたものを使わないかもしれないので、 store にさえ自分で入れてしまえば使わなくても良いのかもしれない
  • とは言え、↑でいろいろと言ってますがNuxt.js側でこの inject の仕組みを用意してくれているのはうれしい。今後の改善にも期待したい

という感じです!

ながーい記事になってしまいましたがここまで読んでいただきありがとうございます。

サイダスではこんな感じにDXを追求しながら堅牢なソフトウェアを作っていく方法について語り合える仲間も絶賛募集中です!

www.wantedly.com

ではでは!

技術ブログを公開しました

サイダス吉田真吾@yoshidashingo)です。

このたびコーポレートサイトのリニューアルに合わせて、サイダス社の技術本部のメンバーによるテックブログを公開しました。

サイダス社における技術者の位置付けについて

サイダスは2011年に人材育成・評価分野の有識者が集まって、現在も代表取締役である松田が起業した会社です。2011年からVPS上で、2012年末くらいにAWSに基盤を移行してB2B SaaSを提供しています。

サイダスにおける技術本部の位置付けは、大きく2つあります。1つめは「すべての働く人がよりよい働きがいを得られる製品機能」を「最適なアーキテクチャで素早くデリバリー」し、「安定して心地よく利用していただく」という事業に対する技術的な貢献ですが、2つめには「徹底的に自分の働きかたにこだわる」という側面です。

事業に対する技術的な貢献

現在技術本部のチーム構成は以下のようになっています。

Center of Excellence(CoE)チーム

  • ITアーキテクト

PdMチーム

  • PdMスタッフ (企画)
  • プロダクトデザイナー
  • プロダクトオーナー

開発チーム

スクラム手法によるアジャイル開発チームが複数稼働していますが、昨年までと違い、PdM&デザイン部分も各チームで担う形を模索しています。

SRE/Productivityチーム

人数が少ないため兼任していますが、SREと情シスを担っているチームです。

チーム構成のポイントとしては、何を作るべきか(What)とどう作るか(How)のバランス、また運用保守ではなく永続的に進化していくための仕組みが備わったチーム構成を指向しています。

徹底的に自分の働きかたにこだわる

特に社内で働きかた含めてあらゆる理論の実践における実験場でありたいと考え、OKRによる目標管理、月イチの1on1、リモートワーク、自己組織化などを技術本部のみで実施している施策があります。

OKR

スクラム手法によるアジャイル開発に慣れてくると、PBIをよりよくチームでこなしてソフトウェアをデリバリーするという方向性においてはある程度満足できるのですが、会社や本部で真にフォーカスしたい尖った方向性に対して、企画〜開発でその輪郭を削り出すような力は生まれないんですね。なので大きい方向性に対して尖らす推進力が欲しいなというモチベーションで開始したのがOKRです。ちなみにすべての活動の最上位概念としてOKRを位置付けています。

今のところ、大半の時間は「本当にフォーカスべき目標の決定とKR(主要な結果:数値目標)」の設定と「個人のOKRの達成が組織のOKRの達成になるための合算量的なリバランス、アラインメント調整」です。ここがうまくできないと実際にうまくいきません。また、アンチパターンとして数値で表せない目標しか設定できないといった場面がありえますが、これを承認してKRで期日管理してもおそらく大半うまくいきません。数値目標に徹底的にこだわりましょう。

月イチの1on1

組織がフラットなので技術本部長である私と全員がそれぞれやる形ですが、毎月必ず1回以上開催することを死守しています。アジェンダは決めてあるので、それに沿って毎月各自からトピックを引き出しています。

現在のところ1on1は本当に強力で、自分では取りかかれないけどきっと改善したら嬉しいとみんなが考えている内容や、取り掛かりかたにモヤモヤ迷っていたことについて整理して自走し始めてくれたり、私に言いにくくてわだかまってたことなどを話してカタルシスを得たりなど効果がとても高いです。

毎回30分でやってるのでタイムマネジメントとすべてのトピックの聞き出しが大変なんですが、昔取得した産業カウンセラーとしての能力が役立ってるように感じます。

github.com

上司として1on1をしなければいけなくなった場合は「アジェンダ」を用意し、「タイムボックスを固定(タイムマネジメント)」し、「コーチングやカウンセリングの技術(特に傾聴)を鍛えたうえで」実施するとうまく回るようになるでしょう。

リモートワーク

現在、東京/大阪/沖縄すべての拠点で1/3程度の技術部メンバーがリモートワークしています。子育てなどの家庭事情もあれば、チームのワーキングアグリーメントで働きやすさを重視してということもありますが、特に条件なくやらせています。また先週は別の事業部でインフルエンザが発生していたため、技術部全員リモートワークにしました。

チームメンバーが隣にいたほうが生産性が高い仕事も時にありますが、集中タイムの確保なども考慮すると、SlackとZoomがあればもはや好きな働きかたを選んでもなんの支障もない状況です。

自己組織化

基本的に私が「あなたが担当している分野については業務命令や意思決定はしない(形式的にすることはあるけど)」と宣言しているので、リーダーシップ原則に基づいて主体的に取り組んでもらってます。その代わり「誰かにアドバイスは必ずもらうこと」としています。

意思決定に必要な情報がもっとも集まっている現場において判断したほうがよいという考えと、正しい意思決定を行うために必要な情報が不足している可能性を当事者では気づけない場合があるためアドバイザリプロセスを入れることで情報量や多面性などを確保するためです。

cydas/principles.md at master · yoshidashingo/cydas · GitHub

※自己組織化は正しい呼び方ではないかもしれない

CTOとしての8ヶ月について

就任してから8ヶ月経過してますが、まだまだマーケットにインパクト与える結果を出せてないのが実感なので、とくに私はCTOである前に「取締役」つまり経営者であるので、しっかりと社会にインパクトを与えていけるように頑張りたいと思います。

yoshidashingo.hatenablog.com

そういえばマネージャーのトリセツも公開しています。

cydas/manager-readme.md at master · yoshidashingo/cydas · GitHub

採用について

サイダス技術本部では今年も積極的に採用を行なっていきます。既存の社員の働きがいを高めてリファラルを強化するという点と、イベント登壇やここのような場所での発信を通じて、みなさまがサイダスで働いてみたいなと興味を持ってもらえるように頑張ります。

www.wantedly.com

www.wantedly.com

www.wantedly.com

それでは