ツールから見るFDPのリモートワーク事情

こんにちは。永和システムマネジメント FDPメンバの坂部です。

今回は、私たちチームがツールをどのように使ってリモートワークしているか、お話しします。

Slack

Slackはこんな感じです。

f:id:sakabehiroki:20220114112430p:plain

主なチャンネルはこんな感じです。(boatってのはチーム名です)

チャンネル名 用途
team42-boat 全体チャンネル。業務連絡や雑談は全部ここ。
team42-boat-log 1日の終わりに、その日やったことを書くチャンネル。次の日の朝会で見返す。
team42-boat-notification GitHubやこのブログの更新通知を送るチャンネル。
times-<名前> メンバそれぞれのつぶやきチャンネル。

多分、そこまで凝ったことはしていないと思います。

唯一特徴的なこととして、スレッドによる返信を非推奨にしています。

メンバ全員が今までSlackをガッツリ使ったことがなかったので、スレッドの返信に慣れておらず、見逃しやすいというのが理由です。小さいチームなので、スレッドなしでもチャンネルがごちゃつくことはありません。

Zoom

チームでは、Zoomで常時接続しています。

質問や共有事項があれば、「ちょっといいですか?」と声をかけることができます。個人作業のときは、接続していても会話せずに作業に没頭しています。

常時接続は、コミュニケーションには便利なのですが、一人で集中したいときにはマイナスになることもあります。よって、強制はしておらず、自由にZoomを抜けることができます。特に私が抜けていることが多いです

f:id:sakabehiroki:20220208092941p:plain

GitHub

コード管理はもちろんのこと、カンバンを中心としたタスク管理にもGitHubを使っています。

f:id:sakabehiroki:20220204173801p:plain

詳しくはこちらをどうぞ。

fdp-blog.hatenablog.com

Miro

ふりかえりにはオンラインホワイトボードのMiroを使っています。

ストックすべき情報はGitHubを中心に管理していますが、ふりかえりをはじめとして、ディスカッションやチームビルディングなど、気軽にふせんをペタペタしたいときにはMiroが重宝します。

f:id:sakabehiroki:20220204173537p:plain

おわりに

どのツールも一長一短なので、自分達に合うツールを自分達で選定することが重要だと思います。

また、日々「こんなツール使ってみたい」「この機能を有効化したい」とチーム内で提案し、より良い使い方を模索していくことも必要だと思います。

今後もツールを使いこなして、効率的にリモートワークしていきたいです 💪

Agile Studio見学への要望を機械学習で分析してみた

はじめに

こんにちは。永和システムマネジメント FDPメンバの坂部です。

今回、Agile Studio 見学への要望のデータを、クラスタリングすることで分析してみました。

Agile Studioとは?

Agile Studioは、永和の、アジャイル開発を推進するサービスです。 企業様に向けて、仕事の様子を見たりアジャイル開発についてディスカッションしたりする見学会を開催しています。 今回分析するデータは、この見学の事前アンケートを元にしています。

www.agile-studio.jp

やったこと

データを整形する

データを眺める

まずは、データを眺めました。

データは、見学する企業様ごとにスプレッドシートにまとまっています。

f:id:sakabehiroki:20220203104527p:plain

複数の項目があるのですが、今回は「ディスカッションで具体的に相談したい事項」について分析することにしました。 なお、これ以降、「ディスカッションで具体的に相談したい事項」は「質問」と呼ぶことにします。

データをファイルにまとめる

そこまで量がなかったので、手作業でスプレッドシートから「ディスカッションで具体的に相談したい事項」を抜き出し(as 質問)、一つのスプレッドシートにまとめ、csvでエクスポートしました。

質問を分割する

ここからPythonで作業していきます。

import pandas as pd

# 先ほど作成したcsvを読み込む
df = pd.read_csv('./data.csv', header=None, names=['質問'])
df

f:id:sakabehiroki:20220202180246p:plain

スプレッドシートのセル一つ一つから抜き出しましたが、セル一つに複数の事項が存在するセルもあったので、下記のようなデータが含まれています。

f:id:sakabehiroki:20220202182325p:plain

これらは、コードで分割しました。

import re

def split_to_single_question(target):

    for bullet_mark in '①', '②', '③', '-':
        target = target.replace(bullet_mark, '・')

    # 「・」の前に改行を含むものだけを対象として分割する。
    # 含まないものは、箇条書きを意味する「・」ではないと捉えて、分割しない
    # 改行と「・」の間の半角スペースは無視する
    pattern = "\n\s*・"

    if re.search(pattern, target) is None:
        return [target]

    target_split = re.split(pattern, target)

    if target_split[0][0] == "・":
        return target_split
    else:
        # 箇条書き一つ前の文は、全ての箇条書きの先頭に付与する
        return [target_split[0] + ", " + t for t in target_split[1:]]

split_questions = []

for questions in df["質問"]:
    for q in split_to_single_question(questions):
        split_questions.append((questions, q))

df = pd.DataFrame(split_questions, columns=["質問", "分割済み質問"])
df

f:id:sakabehiroki:20220202180318p:plain

また、下記のように永和からの回答が含まれている場合もあります。

f:id:sakabehiroki:20220202182355p:plain

これらは、コードにて回答の部分を削除しました。

def remove_response_from_eiwa(target):

    for response_mark in ["⇒(ESM)", "⇒ (ESM)", "→"]:
        target = target.split(response_mark)[0]

    return target


df["分割済み返答削除済み質問"] = df["分割済み質問"].map(remove_response_from_eiwa)
df

f:id:sakabehiroki:20220202180417p:plain

パターンの漏れがあり、これらで全ての質問を分割したり、全ての回答を削除したりはできませんが、 とりあえずヨシとします 👈🏼 🐱

クラスタリング

では、クラスタリングしていきましょう。

質問から単語を抜き出す

まずは、質問から単語を抜き出します。

今回は、Mecabという形態素解析エンジンで品詞に分解しました。 解析用の辞書はIPADICを採用しています。(実はIPADICはサポート終了してる 😭 )

分解後、名詞かつ意味のある単語だけを抜き出します。

import requests
import MeCab
import ipadic

# 今回の処理において意味のない単語リスト(=ストップワード)
stop_words = []

# ある程度のストップワードは、ここでリストが提供されている
SLOTHLIB_URL = "http://svn.sourceforge.jp/svnroot/slothlib/CSharp/Version1/SlothLib/NLP/Filter/StopWord/word/Japanese.txt"
stop_words.extend(requests.get(SLOTHLIB_URL).text.split("\r\n"))

# slothlibで対応できないものは、手動で対応
stop_words.extend(['0', '1', '2', '3', '4', '5', '6' '7', '8', '9'])
stop_words.extend(['0', '1', '2', '3', '4', '5', '6' '7', '8', '9'])
stop_words.extend(['あ', 'い', 'う', 'え', 'お', 'か', 'き', 'く', 'け', 'こ', 'さ', 'し', 'す', 'せ', 'そ', 'た', 'ち', 'つ', 'て', 'と', 'な', 'に', 'ぬ', 'ね', 'の', 'は', 'ひ', 'ふ', 'へ', 'ほ', 'ま', 'み', 'む', 'め', 'も', 'や', 'ゆ', 'よ', 'ら', 'り', 'る', 'れ', 'ろ', 'わ', 'を', 'ん'])
stop_words.extend(['が', 'ぎ', 'ぐ', 'げ', 'ご', 'ざ', 'じ', 'ず', 'ぜ', 'ぞ', 'だ', 'ぢ', 'づ', 'で', 'ど', 'ば', 'び', 'ぶ', 'べ', 'ぼ'])
stop_words.extend(['方', '方法', 'こと', 'ため', '人', '性', '何', '等', '化', '場合', '点', '時', '工夫', '様', '中', 'とき', 'ところ', 'もの', 'それ', '書', '側', '内', '際', '以下', '20', '年', 'M', '内容', '作成', '.', 'どこ', '以外', 'つ', '目', 'さん'])
stop_words.extend(['アジャイル', 'Agile', '開発']) # かなり多く出てくるので削除

mecab_tagger = MeCab.Tagger(ipadic.MECAB_ARGS)

def parse_question_to_words(target):

    parsed_lines = mecab_tagger.parse(target).split('\n')

    parsed_words = [parsed_line.split('\t') for parsed_line in parsed_lines]

    words = []

    for parsed_word in parsed_words:

        if len(parsed_word) > 1 and '名詞' in parsed_word[1]:

            target = parsed_word[0]

            # ストップワードに含まれない単語だけを取り扱う
            if not target in stop_words:
                words.append(target)

    return words

df['単語'] = df['分割済み返答削除済み質問'].map(parse_question_to_words)
df

f:id:sakabehiroki:20220202180439p:plain

質問をベクトル化する

単語をもとに、それぞれの質問をベクトル化していきます。

今回は、それぞれの単語の出現回数を、質問を表すベクトルとしました。

また、そのベクトルをtf-idfで重み付けしました。(tf-idfの説明は、自信ないのでwikipediaに任せます…)

from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import CountVectorizer

# ベクトル化
bags = CountVectorizer().fit_transform([" ".join(words) for words in df["単語"]])

# ベクトルを重み付け
tf_idf = TfidfTransformer(use_idf=True, norm="l2", smooth_idf=True).fit_transform(bags)

質問をクラスタリングする

k-means法でクラスタリングしてみます。(この説明もwikipediaで…)

from sklearn.cluster import KMeans

kmeans_model = KMeans(n_clusters=10, random_state=1) # クラスタ数は10で固定

result = kmeans_model.fit_predict(tf_idf)

結果を描画する

クラスタごとの要素数をグラフで見てみましょう。

import collections
import matplotlib.pyplot as plt

cluster_count = dict(collections.Counter(result).most_common())
cluster_count_sorted = sorted(cluster_count.items(), key=lambda x: x[0])

k = [f"クラスタ {k}" for k, _ in cluster_count_sorted]
v = [v for _, v in cluster_count_sorted]

plt.figure(figsize=(20, 20), dpi=50)
plt.bar(k, v)

f:id:sakabehiroki:20220202180830p:plain

まあまあ分散した結果になったのではないでしょうか。

クラスタごとにどんな単語が含まれているか見てみましょう。

df["クラスタ"] = result

cluster_words_dict = {}

for i, row in df.iterrows():

    cluster, words = row["クラスタ"], row["単語"]

    if cluster in cluster_words_dict:
        cluster_words_dict[cluster].extend(words)
    else:
        cluster_words_dict[cluster] = words

cluster_commonwords_dict = {}

for k, v in cluster_words_dict.items():
    counter = collections.Counter(v)
    # クラスタの頻出頻度上位10単語を参照
    cluster_commonwords_dict[k] = dict(counter.most_common(10))

fig, ax = plt.subplots(10, figsize=(50, 50))

for k, v in cluster_commonwords_dict.items():

    ax[k].set_title(f"クラスタ {k}")
    ax[k].bar(v.keys(), v.values())

f:id:sakabehiroki:20220202180931p:plain

見辛いですね…ワードクラウドを描画してみましょう。

word_cloudを使います。

from wordcloud import WordCloud

fig, ax = plt.subplots(nrows=4, ncols=3, figsize=(50, 50))

for k, v in cluster_commonwords_dict.items():

    wordcloud = WordCloud().fit_words(v)

    ax_item = ax.ravel()[k]
    ax_item.set_title(f'クラスタ {k}', fontsize=100)
    ax_item.set_axis_off()
    ax_item.imshow(wordcloud, interpolation='bilinear')

f:id:sakabehiroki:20220202180957p:plain

なんとなく「クラスタ2はウォーターフォールからの移行に関して」「クラスタ8はリモートでのコミュニケーションに関して」など意味のあるクラスタになってそうですね。

感想

2週間、こちらの課題に取り組んでみたのですが、とりあえず一通りのプロセスを動かすために、アルゴリズムやコードの精査を省いています。それらを精査してもっと適切な方法を学びたいです。

また、実際に課題にトライすることで、機械学習プロジェクトの流れが少し掴めた気がします。

GitHub Projects Betaではじめるプロジェクト管理

こんにちは。永和システムマネジメント FDPメンバの坂部です。

今回は、私たちのチームで、カンバンツールGitHub Projectsの新バージョン、GitHub Projects Betaをどう使っているか、お話しします。

はじめに

私たちのチームは、FDPという特殊なプロジェクトに属しているので、アプリ開発に加えて、勉強や情報発信、イベント参加などもチームのタスクとして管理しています。

それに従い、現在リポジトリは3つになっています。

  • 勉強・情報発信・イベント参加などのタスク管理用
  • アプリ開発用
  • アプリ開発のテンプレート用

アプリ開発用に関しては、2〜3ヶ月で一つのアプリを作り、それを何回か繰り返す予定なので、今後、アプリA開発用、アプリB開発用…と増える可能性があります。

今回、GitHub Projects Betaを採用することで、複数のリポジトリに存在する、さまざまなタスクを管理することができるようになりました。

どう使っているか

メインのカンバンはこんな感じです。

ここにはチームが関係するIssue、PRが全て存在します。

f:id:sakabehiroki:20220127134052p:plain
メインのカンバン

こちらは、アプリ開発用のカンバンです。

先程のカンバンに、リポジトリでフィルターをかけたものになります。

さまざまなタスクに取り組んでいるとはいえ、現在はアプリ開発がメインなので、作業中はこのカンバンを見ることが多いです。特に、朝会やスプリントプランニングでは、ここを中心に議論します。

f:id:sakabehiroki:20220127141202p:plain
アプリ開発用のカンバン

こちらは、アプリのユーザーストーリーだけを表示しているカンバンです。

スプリントレビューやスプリントプランニングで、POとユーザーストーリーの優先度を議論するときに使います。POにとってはノイズになりうるタスクが見えないので、純粋にユーザーストーリーだけを対象にして議論できます。

f:id:sakabehiroki:20220127141619p:plain
アプリ開発のユーザーストーリーのカンバン

こちらは、アプリのプロダクトバックログをリスト表示したものです。

GitHub Projects Betaでは、カンバン以外に、このようにリスト表示することもできます。 プロダクトバックログの優先度を決めて並び替えるときは、一度に多くのバックログを見ることのできるリストの方が便利です。

f:id:sakabehiroki:20220127142311p:plain
アプリのユーザーストーリーのリスト

PRだけのカンバンも作れます。現在出ているPRの状態が一目でわかるので便利です。

f:id:sakabehiroki:20220127143225p:plain
PRだけのカンバン

まとめ

複数のリポジトリを横断するカンバンはGitHub Projects(=新バージョンじゃない方)でも作成できます。

しかし、リポジトリやラベルなどでフィルターする場合、GitHub Projects Betaは便利です。GitHub Projectsでもフィルターをかけることはできますが、GitHub Projects Betaではフィルターをかけた状態でViewとして固定できます。このViewを使い分けることで、普段見るカンバン、スプリントプランニングのときに見るカンバンなどを手軽に切り替えることができます。

また、リストはGitHub Projectsにはない機能です。カンバンは見やすいですが、縦に長いレーンはスクロールしないと見えない場合があります。そういったとき、リストは役立ちます。

今後

GitHub Projects Betaは、名前の通りまだベータ版であり、足りない機能もあります。

例えば、自動的なアイテムの移動(Workflows)は、プリセットを有効化することはできますが、カスタマイズまではできないです。

私たちのチームでは、IssueとPRは、追加されたらどちらも「プロダクトバックログ」レーンに移動します。PRは「In Progress」レーンに移動してほしいのですが、別々の設定はできないので、手動で移動させています。

f:id:sakabehiroki:20220127150629p:plain
Workflows

ありがたく使いつつ、今後の動向を注視していきたいです。

https://github.blog/changelog/label/issues/ 👀

Windowsの開発環境をセットアップしてみる

こんにちは。永和システムマネジメント FDPメンバの坂部です。

先日、MacからWindowsに移行しました。

fdp-blog.hatenablog.com

今日は、私が行ったセットアップをふりかえってみようと思います。

設定したもの

  • WSL
  • Docker
  • Git
  • Homebrew
  • zsh
  • VS Code

ここには挙げていませんが、SlackやZoomも入れました。

WSL

まず、WSLを入れます。デフォルトではUbuntuがインストールされます。

powershell

# see https://docs.microsoft.com/ja-jp/windows/wsl/install
wsl --install

f:id:sakabehiroki:20220106154531p:plain

以降は、WSLで作業します。

Docker

Dockerは、WSLに入れます。

最初はDocker Desktop for Windowsを使っていたのですが、有料化するので、WSLにインストールすることにしました。

WSL

# see https://docs.docker.com/engine/install/ubuntu/

# レジストリを設定
$ sudo apt-get update
$ sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

$ echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Dockerをインストール
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io

Hello Worldしてみます。

WSL

# Dockerデーモンを起動
$ sudo service docker start
 * Starting Docker: docker

# Hello World
$ sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
2db29710123e: Pull complete
Digest: sha256:2498fce14358aa50ead0cc6c19990fc6ff866ce72aeb5546e1d59caac3d0d60f
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

問題なさそうですね。

後ほどVS Codeを使うときに問題になるので、Dockerをルートユーザ以外で扱えるようにします。

WSL

# see https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user

$ sudo groupadd docker
$ sudo usermod -aG docker $USER

WSLを再起動すると、Dockerがsudo無しで使えるようになっています。

WSL

$ docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

Git

Gitは既にインストールされていますが、バージョンが古いので新しくします。

WSL

# Gitのバージョンを確認する
$ git --version
git version 2.17.1

# 最新のGitをインストールする
# see https://git-scm.com/download/linux
$ sudo add-apt-repository ppa:git-core/ppa
$ sudo apt update
$ sudo apt-get install git

# Gitのバージョンを確認する
$ git --version
git version 2.34.1

クレデンシャルを保存するために、Git Credential Managerを使います。

Git Credential ManagerはGit for Windowsに含まれているようなので、Git for Windowsをインストールします。

f:id:sakabehiroki:20220106152026p:plain
ここから手動でインストールしました。

WSLに戻って、クレデンシャルマネージャを設定します。

WSL

# see https://docs.microsoft.com/ja-jp/windows/wsl/tutorials/wsl-git
$ git config --global credential.helper "/mnt/c/Program\ Files/Git/mingw64/libexec/git-core/git-credential-manager-core.exe"

Homebrew

HomebrewはMac OSでよく使われるパッケージマネージャですが、Linuxでも使えます。 これ以降は、Homebrewでインストールしていきます。

WSL

# see https://docs.brew.sh/Homebrew-on-Linux

# Homebrewをインストール
$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

...略...

==> Installation successful!

==> Homebrew has enabled anonymous aggregate formulae and cask analytics.
Read the analytics documentation (and how to opt-out) here:
  https://docs.brew.sh/Analytics
No analytics data has been sent yet (nor will any be during this install run).

==> Homebrew is run entirely by unpaid volunteers. Please consider donating:
  https://github.com/Homebrew/brew#donations

==> Next steps:
- Run these two commands in your terminal to add Homebrew to your PATH:
    echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"' >> /home/hirokisakabe/.profile
    eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
- Install Homebrews dependencies if you have sudo access:
    sudo apt-get install build-essential
  For more information, see:
    https://docs.brew.sh/Homebrew-on-Linux
- We recommend that you install GCC:
    brew install gcc
- Run brew help to get started
- Further documentation:
    https://docs.brew.sh
# 上記の "Next steps" を参考にパスを通す
$ echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"' >> /home/hirokisakabe/.profile
$ eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"

# パスが通っているか確認
$ which brew
/home/linuxbrew/.linuxbrew/bin/brew

zsh

今までMac OSでzshを使っていたので、ログインシェルをbashからzshに変更します。

WSL

# zshをインストール
$ brew install zsh

# パスを確認
$ which zsh
/home/linuxbrew/.linuxbrew/bin/zsh

ログインシェルの候補リストに、zshを追加します。

/etc/shells

# /etc/shells: valid login shells
/bin/sh
/bin/bash
/bin/rbash
/bin/dash
/usr/bin/tmux
/usr/bin/screen
# 下記を追加
/home/linuxbrew/.linuxbrew/bin/zsh

WSL

# ログインシェルを変更
$ chsh -s /home/linuxbrew/.linuxbrew/bin/zsh

WSLを再起動するとzshへの変更が反映されています。

f:id:sakabehiroki:20220106143003p:plain

このあと少し使うので、.zshrcを作るために「0」を入力しておきます。

せっかくなので?zshのプロンプトテーマを設定してみます。

わたしは、pureをよく使ってます。

WSL

# bashに設定したHomebrewのパスを、zshにも設定する
$ echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"' >> /home/hirokisakabe/.profile
$ eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"

# pureをインストール
$ brew install pure

.zshrcを編集してpureを設定します。

.zshrc

# Created by newuser for 5.8

# see https://github.com/sindresorhus/pure
# 下記を追加
autoload -U promptinit; promptinit
prompt pure

WSL

# .zshrcの設定を反映
$ source .zshrc

f:id:sakabehiroki:20220106144512p:plain

pureが設定されました!!ちょっと見辛いかな…

VS Code

普通に公式サイトからインストールします。

FDPではVS Code Remote Containerを使っているので、問題なく動作するか、開発コンテナを立ち上げて確認してみます。

サンプルとして、 https://github.com/microsoft/vscode-remote-try-python を使います。

WSL

$ git clone https://github.com/microsoft/vscode-remote-try-python.git

$ cd vscode-remote-try-python

# カレントディレクトリでVS Codeを開く
# see https://docs.microsoft.com/ja-jp/windows/wsl/tutorials/wsl-vscode#from-the-command-line
$ code .

Reopen in Containerします。

f:id:sakabehiroki:20220114143340p:plain

開発コンテナを立ち上げることができました!!

f:id:sakabehiroki:20220114143441p:plain

おわり

↓ Dockerデーモンを自動で起動するようにしたり、dotfilesをMacOS用と共通化したり、やってみたいことはまだまだあります!!

qiita.com

qiita.com

参考

コマンドラインからカレントディレクトリをdevcontainerで開く

こんにちは。永和システムマネジメント FDPメンバの坂部です。

今まで、VS Code Remote Containerを使うときは、ターミナルで任意のディレクトリまで移動して、code .でVS Codeを開いてから、Reopen in Containerしていました…

devcontainer CLIをインストールすれば、devcontainer openすることで、Reopen in Containerを省略して、一発で、カレントディレクトリをdevcontainerで開くことができます。

# カレントディレクトリをdevcontainerで開く
$ devcontainer open

2022/01/12時点では、コマンドパレットからインストールしてください。

f:id:sakabehiroki:20220112175645p:plain

npm経由でもインストールできるのですが、現在、npm経由でインストールしたものはdevcontainer openコマンドが使えなくなっています。

Issue : https://github.com/microsoft/vscode-remote-release/issues/5957

ちなみに、このdevcontainer CLI。もう一つ、devcontainerのイメージをビルドするという機能があるのですが、今のところ使っていません。こちらの機能も使っていきたいなあ…

参考

教育目的プロダクトのプロダクトゴールをどう導くか

POの岡島です。

※ これは、「育てるAI検温(二期生版)」のプロダクトビジョン、および、プロダクトゴールを説明した文書です。今までメンバーに口頭で説明していたものを、これまでの経緯もふまえ整理しました。当然ハイコンテキストな読み物なのですが、世のScrumチームの何か役に立つ部分もあるかもと思い、ほぼそのまま、ブログエントリとして公開します。

まずは、FDPは今は二期生が活動中で、今から二期生は一期生と同様のビジョンで「育てるAI検温」というプロダクトを開発するんだ、ということだけ理解してもらえればうれしいです。PO視点でのFDPの説明については、以下エントリにあります。

fdp-blog.hatenablog.com

 

続きを読む

教育目的プロダクトのプロダクトオーナーの仕事

岡島です。こんにちは。

私はFDP(Future Design Project)のプロダクトオーナーをやってます。FDPは、社員の技術獲得を目的とした教育プロジェクトで、プロダクト作りを通じて、機械学習の実践的なスキルを獲得することがゴールです。

このブログでは、私がFDPで開発されるプロダクトのプロダクトオーナーとして、具体的にどんなことを考え、どんなことをしているのか、なるべくわかりやすく説明していくつもりです。

これまで開発してきたプロダクト

FDPは今年で二年目を迎えるのですが、これまでに、次のようなプロダクトを開発してきました。

  • 機械学習とARによる帽子着せ替えカメラアプリ
  • 機械学習で脇温度を推定する非接触型体温計(育てるAI検温)

f:id:HappymanOkajima:20220111002112p:plain

育てるAI検温

いずれのプロダクトも教育目的であり、「プロダクトが売れそうか」ではなく、「メンバーが何を学べるのか」を重視して、コンセプトや利用技術を選んでいます。

また、いずれのプロダクトもScrumを採用しており、今後開発するプロダクトもその予定です。

教育目的プロダクトならではの難しさ

実際にプロダクトオーナーを一年間やってきて感じたことは、教育目的のプロダクトは、バックログアイテムとその優先度や、プロダクトビジョン、プロダクトゴールの設定が意外に難しいということです。

フィーチャーとしての新規性や製品の完成度(品質)よりも、経験・獲得できる技術の内容を優先する必要がありますが、あまりにありきたりなスペックだと、メンバーのモチベーションも上がらず、結果的に教育効果を下げてしまうことになりかねません。

また、あまりに野心的な目標設定をしたり、メンバーにとって関心や面白みを感じられないテーマでもうまくありません。

プロダクトのビジョンやゴールに関しては、やはり「そもそも、私たちは、なぜこのような活動をしているのか?」という根源的な意義に立ち返って考えていく必要があります(※ 具体的な内容については、次回以降の記事で詳しく説明していく予定です)。

組織に知識を蓄積していくために

ちなみに、FDPは毎年メンバーが全員入れ替わるため、組織的に知識を蓄積していくような仕掛けが必要です。そこで私は、野中郁次郎先生らのSECIモデルを参考に、意識的に暗黙知と形式知が相互変換され、スパイラルに、より質の高い知識が広まっていくような取り組みを模索しています。

詳しくは、2022年1月5日~7日に開催された、Regional Scrum Gathering Tokyo 2022 にて発表させていただいた資料をご覧ください。

 

ではまた!