Rails アプリケーションのER図(SchemaSpy)をCircleCIで定期出力する

業務にてRuby on Railsの Webアプリケーション開発していて、DB設計資料やER図を必要に応じて手で作って説明に利用したりしていました。 システムの運用も始まり、そろそろ手で作ることによる更新が厳しくなってきた感じがするので、自動生成できないかなあと思っていました。

今回は、CIプロセス(CircleCI)の中でSchemaSpyというツールを用いて、ドキュメントを自動生成してみたので、学びを書き残しておきます。

f:id:zuckey_17:20210107070346p:plain:w300

SchemaSpy

schemaspy.org

SchemaSpyというものを見つけました。

テーブル、カラムの構成情報やER図といったドキュメントをHTML/JS/CSSとして静的に出力してくれるツールです。実際にサーバー上で動いているDBを指定するとドキュメントを生成してくれます。

Javaで作られていているツールですが、公式でDockerコンテナイメージを公開してくれているので、簡単にCircleCIなどで実行できます。

手元で動かす

まずは、Rails アプリケーションのバックエンドとなっている MySQLが立ち上がっている状態で、そのDBに対して SchemaSpyの作成コマンドを実行してみました。

docker コマンドが動く環境で、以下のようにたたきます。

(手元の環境は、mac OS 10.15.7 で、MySQL 5.7でした)

$ docker run -v "$PWD/schema:/output" --net="host" schemaspy/schemaspy:snapshot -debug -t mysql -host 127.0.0.1:3306 -db 【DB名】 -u 【ユーザー名】 -p 【パスワード】 -connprops useSSL\\=false -s 【DB名】

すると実行したディレクトリ直下に schemaディレクトリが作成され、その中の index.html をブラウザで開くと作成されたドキュメントを確認することができます。

簡単ですね。

最新のドキュメントをいつでも見たい

ここまで来ると、後はDB構成に変更があってもいつでも最新のドキュメントを用意しておきたい気持ちになります。

最終的には冒頭でも書いたとおり、CircleCIで実行発行してホストすることになりましたが、Railsアプリケーションでやろうとするといくつか問題もありました。その紆余曲折含めて最終的にどう実現したかを紹介します。

コマンドを実行する対象のDBを準備する必要がある

ドキュメント作成時、実際にサーバー上で動いているDBを指定する必要があります。

最新のドキュメントをいつでも見られるようにするための、選択肢としては以下の3つくらいを上げていました。

  • A案)CI環境でドキュメント作成用のDBを一時的に立ち上げる
  • B案)ステージング環境などのDBに定期的にコマンドを実行する
  • C案)スキーマに変更があったとき毎回ローカルのDBに対してコマンドを叩く

C案は自動化とはいえなさそうなので却下して、A案かB案になります。
わざわざこのために実行環境を用意するのも嫌だなという気持ちになって、最終的にA案に落ち着きました。

SchemaSpyでは、稼働しているDBに対して実行することでテーブルごとの行数も見れますが、本番の情報ではない限りその情報はあまり意味もないので、すべてのテーブルが0行になっていてもドキュメントとして致命的な問題ではないということにしました。

f:id:zuckey_17:20210107071854p:plain

今回はCircleCI上で実現しようということで、最終的なコードとしては、以下のようなjobの定義になりました。

(省略)

build-erd:
    docker:
      - image: schemaspy/schemaspy:snapshot
        user: root
      - image: mysql:5.7.12
            <<: *dockerhub_auth
            environment:
          MYSQL_DATABASE: xxxx_test
          MYSQL_USER: root
          MYSQL_ROOT_PASSWORD: password
    working_directory: ~/repo
    steps:
      - checkout
      - run: apk add openssl mariadb-client
            - run: sleep 5
      - run: cat ./db/structure.sql | mysql -h 127.0.0.1 -P 3306 -u root -ppassword xxxx_test
      - run: cd / && schemaspy -t mysql -host 127.0.0.1 -port 3306 -u root -p password -db xxxx_test -s xxxx_test -hq -connprops useSSL\\=false
      - store_artifacts:
          path: /output

(省略)

MySQLのコンテナを傍らで動かしつつ、SchemaSpy のコンテナの中でいくつかの処理を実行することになります。

Railsスキーマフォーマットを変更する

SchemaSpyのコンテナからMySQLコンテナに対して、何らかの方法でSQLを実行し、DBを準備する必要があります。

SchemaSpyを利用されている例をいくつか調べてみると、DDLの形式のSQLファイルを手元でdumpしてリポジトリに含めておく、という運用をされているのが観測できました。
ローカルのDBをもとにダンプしてしまうのは、結局手で運用する部分がのこったり、開発者の考えることが増えると考えました。

Railsのデフォルトだと、db/schema.rb というスキーマファイルを bin/rails db:setup(の中のbin/rails db:schem:load)で読み込んで反映することになります。しかし、これをそのままやろうとすると、SchemaSpyのコンテナにRubyの環境を構築する必要があり、すこしつらそうという印象を持ちました。

他の方法を検討すると、Rails には db/structure.sql という別のスキーマフォーマットがあることに気づきました。

Railsには、config.active_record.schema_formatという設定があり、:sql:rubyを選択でき、それによって、structure.sqlschema.rbの違いが出てきます。

デフォルトは:rubyですが、config/application.rbに以下のように書くことによって、設定を変更することができます。

class Application < Rails::Application
  config.active_record.schema_format = :sql
end

ドキュメント自動生成のためにスキーマフォーマットを変えるのはどうかな、とも思いましたが、調べてみると、デメリットとしては手元でのスキーマファイルのコンフリクトが少し難しくなる程度でした。 structure.sqlでは、むしろメリットとして schema.rbでは表現しきれないDBの機能まで表現できるということで、特にこの変更は問題ないだろう、というふうに考えました。(実際そういった高度なDBの機能は使っていませんが。) *1

Active Record マイグレーション - Railsガイド
Pros and Cons of Using structure.sql in Your Ruby on Rails Application | AppSignal Blog

これによって上記のようなjobの設定で、CircleCI上にSchemaSpyで生成したドキュメントをホストすることができました。

その他

sleep 5 ?

job の設定で、 sleep 5という謎のコマンド実行があります。

f:id:zuckey_17:20210107063806p:plain

上記のようなエラーが出てしまい、MySQLが立ち上がりきっていない段階でコマンドを実行してしまっているのかなと考えて、足しています。

https://circleci.com/docs/ja/2.0/executor-types/

CircleCI公式のドキュメントでもmongoの立ち上がりを待っているような記述もあって、それに習ってこの対応をしていますが、5秒以上かかったらどうするんだ、という気持ちがあるので、あるべきやり方をご存知のかたいらっしゃったら教えてほしいです。

そんなに頻繁にスキーマ変更するの?

pushごとに毎回ドキュメントを生成するのかという話があると思います。確かにそんなに頻繁にスキーマの変更は起こらないので、追加で db/structure.sqlの変更差分があるときのみドキュメント生成を実行するということにしました。

CircleCIにはスケジュール実行の機能があったりするので、そちらを利用しても良いかもしれません。

終わり

これで、Rails の DB関連のドキュメントを自動メンテナンスする基盤が整いました。参考になると嬉しいです。

今のまま、CircleCI上にホストすると、せっかくのドキュメントの導線が埋もれてしまうので、ドキュメントを生成したタイミングでSlackなどにリンクを通知したり、S3などに上げてURL固定で最新のドキュメントを参照できるようにしたりなどもう少し改良の余地があるかなと思っています。

参考

https://engineering.mercari.com/blog/entry/2018-05-25-133818/

*1:6.1系のRails までは、bin/rails db:structure:dump というコマンドを実行することにより、スキーマフォーマットの設定を変えることなく structure.sqlを取得することができますが、6.2よりこの機能は削除され、設定フォーマットのスキーマファイルのみ取得できる、ということになっています。

【Golang】 Airを用いてWebアプリケーション開発中に変更を即座に反映する

github.com

Golang で Webサイトを作ろうと gin を触っていると、ライブリロードの機能がなく、変更が即座に反映されなくて、面倒くさく思っていました。 Air というツールがよく使われていそうだったので、使ってみました。

このエントリはそのときのメモです。

gin アプリケーションの準備

まず、Air と関係のない部分で最小のアプリケーションを用意します。

Golang バージョンは以下の通りで、環境は macOS Catalina (10.15.7) です。

$ go version
go version go1.15.5 darwin/amd64

目指すフォルダ構成は以下です。

.
├── go.mod
├── go.sum
└── main.go

フォルダを作成し、go modules で環境を構築します。

$ mkdir gin-air-example && cd $_
$ go mod init gin-air-example
go: creating new go.mod: module gin-air-example
$ touch main.go

main.go の内容は以下とします。

package main

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    r.GET("/", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "message": "OK",
        })
    })

    r.Run(":3000")
}

localhost:3000 にアクセスしたら、 {"message": "OK"}というJSONを返します。

$ go run main.go
go: finding module for package github.com/gin-gonic/gin
go: found github.com/gin-gonic/gin in github.com/gin-gonic/gin v1.6.3
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /                         --> main.main.func1 (3 handlers)
[GIN-debug] Listening and serving HTTP on :3000
$ curl localhost:3000
{"message":"OK"}

f:id:zuckey_17:20210104004434p:plain

このように、デバッグのログにもアクセスが来たことが表示されます。

main.go を編集する

ここで、main.goを以下のように変更します。

// (省略)

func main() {
    r := gin.Default()
    r.GET("/", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
-          "message": "OK",
+           "message": "Hello World",
        })
    })

    r.Run(":3000")
}

main.go を保存して、再度 localhost:3000にアクセスしても結果は変わりません。

この際、go run main.goを一旦止め、再度同じコマンドを実行すると出力結果は変わります。

$ curl localhost:3000
{"message":"Hello World"}

しかし、普段開発していて毎回 コマンドを叩き直すのはやめたいです。ここで Air というツールを導入します。

Air を導入する

Air を installします。

$ go get -u github.com/cosmtrek/air

.air.toml ファイルを作成して、設定ファイルを公式の例)からコピーし、ペーストします。

# Config file for [Air](https://github.com/cosmtrek/air) in TOML format

# Working directory
# . or absolute path, please note that the directories following must be under root.
root = "."
tmp_dir = "tmp"

[build]
# Just plain old shell command. You could use `make` as well.
cmd = "go build -o ./tmp/main ."
# Binary file yields from `cmd`.
bin = "tmp/main"
# Customize binary.
full_bin = "APP_ENV=dev APP_USER=air ./tmp/main"
# Watch these filename extensions.
include_ext = ["go", "tpl", "tmpl", "html"]
# Ignore these filename extensions or directories.
exclude_dir = ["assets", "tmp", "vendor", "frontend/node_modules"]
# Watch these directories if you specified.
include_dir = []
# Exclude files.
exclude_file = []
# Exclude unchanged files.
exclude_unchanged = true
# This log file places in your tmp_dir.
log = "air.log"
# It's not necessary to trigger build each time file changes if it's too frequent.
delay = 1000 # ms
# Stop running old binary when build errors occur.
stop_on_error = true
# Send Interrupt signal before killing process (windows does not support this feature)
send_interrupt = false
# Delay after sending Interrupt signal
kill_delay = 500 # ms

[log]
# Show log time
time = false

[color]
# Customize each part's color. If no color found, use the raw app log.
main = "magenta"
watcher = "cyan"
build = "yellow"
runner = "green"

[misc]
# Delete tmp directory on exit
clean_on_exit = true

そして、gin のプロセスを止め、airコマンドからWebアプリケーションを起動します。

$ air -c .air.toml

すると、main.go への 変更が即座に反映されるようになったのがわかるかと思います。

これで Air が導入できました。

gin アプリケーションの構築以外でやったことは、以下のみなので、簡単です。

  1. Abr の インストール
  2. 設定ファイルの追加

必要に応じて、.air.toml を修正して利用します。

最終的にファイル構成はこうなっています。

.
├── .air.toml
├── go.mod
├── go.sum
└── main.go

Docker で利用

Docker で利用する場合のメモも載せておきます。

Dockerfile を作成

FROM golang:latest

COPY ./ /go/app

WORKDIR /go/app/

RUN go get -u github.com/gin-gonic/gin
RUN go get -u github.com/cosmtrek/air

CMD ["air", "-c", ".air.toml"]**

docker-compose.yml を作成

version: '3'
services:
  app:
    container_name: app
    build:
      context: ./
      dockerfile: ./Dockerfile
    ports:
      - 3000:3000
    tty:
      true
    volumes:
      - ./:/go/app

アプリケーションを立ち上げます。

$ docker-compose up —build 
Building app
Step 1/6 : FROM golang:latest

# 省略

Successfully built 6e9649de9e6b
Successfully tagged gin-air-example_app:latest
Recreating app ... done
Attaching to app
app    | 
app    |   __    _   ___  
app    |  / /\  | | | |_) 
app    | /_/--\ |_| |_| \_ v1.12.1 // live reload for Go apps, with Go1.14.0
app    | 
app    | mkdir /go/app/tmp
app    | watching .
app    | !exclude tmp
app    | building...
app    | running...
app    | [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
app    | 
app    | [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
app    |  - using env:  export GIN_MODE=release
app    |  - using code: gin.SetMode(gin.ReleaseMode)
app    | 
app    | [GIN-debug] GET    /                         --> main.main.func1 (3 handlers)
app    | [GIN-debug] Listening and serving HTTP on :3000

2回目移行は --build オプションは不要です。

これで同様にmain.goの変更が即座に反映されるようになります。

最終のファイル構成はこちらです。

.
├── .air.toml
├── Dockerfile
├── docker-compose.yml
├── go.mod
├── go.sum
└── main.go

終わりです。

2020年の振り返り

2020年初めてのブログです。 備忘のため。

f:id:zuckey_17:20201231234023j:plain

スタディストに転職し、EMになった

1月にスタディストという会社に転職しました。

エンジニアになってからお世話になっている人もいらっしゃったり、新規事業のリードエンジニアに、というお話をいただいたりしてそこに興味を持ったので決めました。 実際ガッツリ0→1のフェーズを任せていただけたので、慌ただしく楽しい1年になりました。
オンボーディングが終わった2月から構想段階にガッツリ参加し、11月に正式リリースにこぎつけました。

新規事業のPoC

新規事業を始めるにあたって、PoC(Proof of Concept)というのをやりました。
一言にPoCといってもやり方はいろいろあると思うんですが、僕らは実際にちゃんと動くプロトタイプをガッツリ作って未来のお客様に使っていただきフィードバックをもらうというプロセスを選択しました。
実際、Google SpreadSheet をバックエンドにしたものから、それを全部壊して次に firebase で仕様を変えて作り直したりしました。
to Bのサービスなので未来のお客様に実際に使ってもらって、そのお客様の課題が用意した機能によって本当に解消されるのか、というのを本番で作り込む前に検証できるというのは、自分たちの方向性に自身も持てる良いプロセスだったと思いました。

一方で難しいこともありました。
プロトタイプを実際に業務の中でお客様に使ってもらっているため、しっかり本番バージョンへの移行までをやりきる必要があります。 そのため、PoC段階で出た、いろいろなアイデアを取捨選択して、宣言したタイミングにリリースを仕切ることが求められました。
その際に、データを移行しないということや、プロトタイプにはあるこの機能は本番の初期リリースでは実装されないということなど、biz やお客様としっかり握っておく必要がありました。
to Bだと、商談の期間が長くなることも多いです。 そのため、初回の商談ではプロトタイプについての案内をしていたにも関わらず、途中から本番バージョンの話をしているということもよくあり、お客様の目線ではなにの話をしていて、自分たちはどういう準備をしないといけないのかと言うのを掴みづらくなります。
こういった混乱を防ぐため、まだ作っている最中の リリースしていない段階から、営業には仕様や案内時の注意点を事前に説明しておく必要がありました。

振り返って書いていると、to Bの開発ではよくあることな気もしますが、それにリードとして関われたことによる視野の広がりを感じました。

EMになった

上記のリード部分での他チームとのやり取りや、新規事業のエンジニアの採用についても携わっていたこと、会社の組織的な変更タイミングなど、さまざまな要素があって、入社後初の評価のタイミングでエンジニアリングマネージャーになることになりました。 もともと入社のタイミングからふんわりと話はしていたものの、新規プロダクトの立ち上げでガッツリコードを書いたり、設計したりと、プレイヤー業務が多そうだったでタイミングを見ていました。

現状でいうと、とりあえず自分がやっていた仕事をデリゲーションして、マネージャーがやるべきことをする時間を作り出すということを目標としています。
そのために自分のみに集まりがちな情報を適切にメンバーにも見えるようにして、情報の非対称性をなくしたり、手元でいうとモブプログラミングやモブ設計などを取り入れ暗黙知のチーム内での共有をしたりというアクションを取っています。

まだまだ手探りでやっている部分が多いので、なれてきて身についたことがあれば、何かしらアウトプットをしていきたいなと思ってはいます。

副業をしっかりめにやってみた

本業以外でいうと、年始あたりは個人プロジェクトでいくつかサービスを作ったりしていましたが、夏前くらいに副業をもう一度ちゃんとやってみたいなと思い立って、今年後半は2つの副業に携わりました。
これまで副業の経験は、学生時代の友人の会社を少し手伝ったりしたくらいでしっかりとやっていなかったなという感覚がありました。

マイルストーンベースのプロジェクトでレガシーRoRに機能を追加した

1つめの副業として、クラウドワークスという大手クラウドソーシングサービスを利用し、自分で探して仕事を受けてみました。 Ruby on Rails ベースの開発が自信を持てるのでそれで探すとちょうど 1つ良さそうに見えた募集があり、応募してみるとサクッと決まって、思い立ってから 3日で副業が決まってそれは少しびっくりしました。
その案件は、ざっくりいうと、「動いているRoRサービスに機能を追加したいが、テストもないしすべての実装を把握している人もいないためすぐにはできない。まずは必要な部分のテストを追加して、直近ほしい機能を追加してほしい。」という趣旨でした。

  1. テストを追加
  2. 機能を改修、細やかな修正
  3. ほしかった機能を追加

という3つのマイルストーンをおき、そのマイルストーンごとに決まった金額をいただくという形での契約になりました。

技術的な話でいうと、比較的レガシーな、自分以外が書いたコードを理解してテストを書いたり、使ったこともないライブラリやあつかったこともないドメインについて頭に入れて影響範囲を少なくして安心してリリースできるようにすすめたりという部分が勉強になりました。
特に、本業で0→1のプロジェクトをやっていて自分のなかにある知識でほぼすべての機能を実装していたため、そういう難しさを求めていたタイミングでもあり、ちょうど良かったなという気がします。

副業とモチベーション

2つめの副業は11月くらいから友人の紹介で関わり始めたものなので、あまり書くことはないですが、こちらはフロントエンドの細かな修正がおおく、普段の僕の業務ではあまりやらない部分の実装のため、勉強になるなと思いながら取り組んでいます。
副業は自分のcanを残しつつ、少しだけコンフォートゾーンをはずれた部分の業務をすることで、学習サイクルのドライバーにもなるため、続けていきたいなと思いました。

一方で、振り返ってみると、金銭的なことについて今後もう少しちゃんと考えていかないといけない、という課題が残りました。
1つめの副業は、マイルストーンごとに決まった金額をいただくということだったということもあり、難しい機能の実装に時間がかかってしまうと時給換算するといただける額が少なくなってしまいました。
また、2つめの副業についても当初1つめの副業でかかったベースの時給を提示してしまったため、頑張ったなというわりに額が伴わないんだなというふうになってきています。 副業のモチベーションは、本業では得られない技術的興味を満たせるということで一定保てる反面、頑張る気持ちになるための一定の報酬をちゃんと設定する必要性を感じました。

しがないラジオをちょっとお休みしていた

僕はしがないラジオというPodcastのパーソナリティおよび編集をかれこれ4年弱ほどやっていますが、今年はこれまでと比べて活動を緩やかにしていました。
夏前の結婚により生活リズムがかわった、コロナ禍でゲストと対面で収録できなかった、副業が思ったよりタイトなスケジューリングになってしまった、などほかにもいくつかあります。

これによって、しがないラジオを起因として細くも続いていたアウトプットが一切なくなってしましました。 アウトプットによってフィードバックをいただけたりすることが、楽しみの一部であり、成長の要因だったなとおもうので、来年はしがないラジオに限らず、アウトプットを増やしていけたらよいなとおもっています。

具体的には、2019年に執筆した同人誌『チーム開発一年目の教科書』を追記修正して、第2版としたいなと言う気持ちがあります。
副業の序盤で、調査のドキュメントをしっかり書いたり、コミットの分割やPRの説明などをしっかり書いたりすることにより、スムーズに受け入れてもらえたという手応えがありました。
この同人誌の内容はやはり価値がありそうだな、ということをもう一度実感し、一方で読み返してみるともっとよくできる部分が沢山あるなと思ったためです。

ほかにも、まずはカジュアルにまずはアウトプットする習慣を戻していきたいなと思っています。

来年はもう少ししがないラジオの頻度も上げて行きたいなと思っているので、もし興味あるかたいらっしゃいましたら、初めての方でも、これまですでにでていただいた方でも、ぜひ Twitter DMハッシュタグ #しがないラジオにてお声がけください。

おわり

良いお年を!

君は目の前の簡単な問題を興味の対象だと勘違いしてないか?

こんにちは、#しがないラジオ パーソナリティの zuckey です。

この記事は「#しがないラジオ Advent Calendar 2019」25日目の記事です。最終日ですね!(完全に忘れており、ギリギリの公開になりました。)

adventar.org

この1年を経て「ここは自分の弱点だな、直して行きたいな」と思ったことを書いていきます。 読み返したらちょっと自分語りすぎるか、と思う部分もあったんですが、誰かの参考になればいいなと思っています。

この1年で自分の置かれたフェーズが変わってきたように感じ、それをできる限り客観的に考えてみました。 その思考の中に、これから先仕事を楽しんでいくための道標のように思えてきました。

導入

目先のことばかり考えている次期はもう終わりだよ。そろそろ、少し高い所から遠くを見る時がきたんだよ。 SHIROBAKO ロロ

f:id:zuckey_17:20191225220314j:plain:w300

去年(2018年)、僕はしがないラジオの中でことあるごとに「強いエンジニアになりたい」と言っていましたが、今年に入ってそういうことがなくなりました。 これは、今年の頭に公開された以下のインタビュー記事がきっかけだったかなと思います。

pr.forkwell.com

この記事の中で僕は概ね以下のようなことを言っています。

  • 職業エンジニアになったはITのイテレーションサイクルの早さに魅力を感じたから
  • 「Response is King」業務上の反応スピードを早くしたらうまくいく
  • 楽しいって思うことに飛び込めばエンジニアとして強くなれる

僕はせっかちな性格で、やったことへのフィードバックが早ければ、やる気をキープしてどんどん続けていける、みたいなところがあります。*1 この記事にはそのあたりがわかりやすく出ている気がします。 目の前に現れる問題に対応していけば楽しく仕事ができ、結果強くなれる、とそんなふうに考えることによって、「強いエンジニアになりたい」という考えにならなくなっていました。

一方で、現在の会社に入ってからこれまで僕は比較的大きめのプロジェクトにアサインされることが多く、フィードバックのサイクルが長くなりがちでした。 楽しくないと思ったことはないですが「これまでのやり方ではなんかうまくいかなくなってきたぞ」という感覚が徐々に強くなってきました。

目の前の仕事

ユーザーサポート業務が強みだと思っていた

さかのぼって社会人1年目、まだコードを書く仕事をしていなかった時期の話です。 僕は富士通という会社でIaaS事業のサポートデスクを改善する部署に配属されました。 インフラの「イ」の字も知らない僕がいきなりIaaSのサポートに入ってうまくやれるか不安でしたが、サポートデスクはすごく良い経験となりました。 サポートに入ってくる質問をベースにひたすら調べて正しい情報を返す、これによりIaaSサービスの全体像を知ることができました。 機能の詳細や各コンポーネントの関係、ユーザーの利用方法などです。 IaaSに関わらずサービスや製品のドメイン知識をインプットするにはサポート業務をするのが良いのではないか、という成功体験になりました。

僕の2社目はtoBSaaSを提供する会社でした。 この会社から僕はコードを書く仕事をすることになります。 toBのサービスの運用初期では顔のわかる営業や顧客から直接開発者にインシデントや不具合のレポートが届くことも多いです。 僕は、やはり開発者としてはまだ初心者だと思っていたため、1社目の経験を活かし、レポートに対して「誰が/いつまでに対応するか」「一次回答のめど」「回避策や応急措置の有無と方法」「原因と恒久対応の方針」などを整理し、できる限り早く打ち返すことを意識していました。 こうしたことは、先述のようなドメイン知識のインプットだけでなく、営業、顧客からの信頼獲得繋がりました。 これは納期の遅延などマイナスなことが起こったときにその影響を最小限に抑える、というような効果もあったように思います。

3社目は社内向けの管理画面をメインに開発していました。 するとサポート業務の対象は社内のユーザーがメインになります。 社内ユーザーをサポートすると、感謝が直接すぐにかえってきます。 これは実のところすごく気持ちがよく、すごくデキる開発者になったような気になります。 これが罠でした。

ユーザーサポートは麻薬

ここで僕が2019年4月に開催された技術書典5で頒布した「チーム開発1年目の教科書」「7.4社内ユーザーのサポートは麻薬」の項目に、以下のような文があります。

チームに参入した直後、自己肯定感の低い状態ではこれ(サポート)を愚直に行うことでチームや組織への価値を提供できたという成功体験になり、次のステップに踏み出しやすくなります。

一方で、このような対症療法的な対応は比較的簡単です。エンジニアに本来求められることは、そういった問題が起こらない仕組みを構築することです。 個人で対応をして気持ちの良いことをずっと続けるのは中毒性があります。 ただ、本当に進めるべきことは、問題の起こらないシステムを構築する技術力をつけたり、チーム全員で問題に対応するための仕組みづくりをしたりすることだと言うことを忘れないでおきましょう。

今年に入ったあたりから僕は対症療法的な問題解決ではなく、原因療法的な問題解決をより求められるようになっていました。 しかし、自分で本に書いているにも関わらず、思ったようにこの方向にシフトすることがうまくできませんでした。

なぜうまくシフトできなかったのか?

多くの場合、原因療法的な解決は対症療法的な解決よりも難しいものです。 そのため長い時間もかかり、結果としてそのタスクの結果のフィードバックが遅くなります。 ときには対応が遅くなっていることについてユーザーの不満がたまる場合もあります。 分かりづらい原因を取り除くので、それが解決されたとユーザーに分かりづらかったりもします。 そういうときに、僕のモチベーションは下がってしまい、これを抑えるために目の前の問題を片手間で対処してしまっていました。そして本来求められる業務を正しく導くための時間が削られます。

この事実に僕は自覚的になれていませんでした。 「なぜかうまくいかず、どんどん状況が悪くなっているような気がする」と、そう思うようになってしまいました。

ユーザーの声にすぐに対応すること(目の前の簡単なこと)
= 自分がやって楽しいこと 
= 自分が興味があること

だと勘違いしていました。 続けていると、「自分が楽しく働けない、やってもやっても芳しい成果が出ず、成長を感じられない状態」になってしまっていました。

事前に必要な情報を集めて問題を分割する

最後にこのような状態を抜け出すために、こうしていこう、と考えていることをざっくりと書いておこうと思います。

本エントリに書いたようなことに気づかないうちは、設計の本を読めばいいの?マネジメント系の本を読めばいいの?とちょっとインプットの方向性を変えることでうまくいくのではないか、と考えていました。 また、インプットというと特定の領域について網羅的に本をザーッと読む、ということが多かったです。

原因療法ができるようになるには、もちろんそういった設計力や知識の絶対量を根本から伸ばしていくということも必要かと思います。 が、今の僕にはそれよりも、大きな問題に関連する情報をかき集めて小さく分割する能力、が必要だと考えています。 問題を小さく分割すると、フィードバックサイクルも早くなり、結果として長く集中できるのではというプロセスです。

これまでのようなインプットも続けつつ、特にその問題解決に手をつける前に「他社の事例やその技術についての知識やたくさんの関連情報に集中的に当たる」ということに意識的に時間をかけていこうと考えています。

まだすぐには完璧にいきませんが、徐々にその意識はつきつつあります。

まとめ

楽しく働くということを勘違いしていた話とそれを改善するための作戦について書きました。 その考え方、いいね、このあたりが違うんじゃない?というのがあれば、何らかの形でコメントいただければ嬉しいです。

また、僕には幸い「しがないラジオ」というPodcastがあります。 エンジニアを中心としたゲストを呼び、その人の関心ごとをヒアリングしたり深ぼったりしています。 同じような悩みを抱えている(いた)方も多いのではないかなと思っていて、その人達とこの話題について話したいなと思っているので、もし興味あるかたいらっしゃいましたら、 Twitter DMハッシュタグ #しがないラジオにてお声がけください。


ということで、#しがないラジオ Advent Calendar2019 最後の記事でした!良いお年を!

*1:しがないラジオもそういう側面が強く、日々フィードバックをいただいているリスナーの皆さんにはすごく支えられています。感謝。

社内Podcastのすゝめ

こんにちは、#しがないラジオ パーソナリティの zuckey です。

この記事は「#しがないラジオ Advent Calendar 2019」17日目の記事です。

adventar.org

そして本日は、しがないラジオ meetup の第4回目でもあります。 どうだったでしょうか。

shiganai.connpass.com

社内Podcast

しがないラジオのエピソードの中でも話しましたが社内Podcastを立ち上げました。 数日前 Twitter で社内 Podcast について会話が盛り上がったので、書いておこうと思います。

また、同AdventCalendarの14日の記事でも、始めたとおっしゃっている方がいたので、盛り上がりを感じますね。 本エントリでは社内Podcastがどうよくて、それをどんなふうに実現したか、という体験談を書きます。 これから始められる人の参考になれば。*1

目次

  • なぜ社内Podcast
  • コンテンツ
  • 実装方法
  • まとめ

なぜ社内Podcast

"社内" Podcastといっているのは、文字通り社内にのみ公開しているからです。 社外の人はその音声を聞くことはできません。 そのため、社外に出すにはセンシティブなことや、内輪ネタ、社内用語も気にせず話せます。

音声メディアというのは、文章やスライドなどに比べてその場の空気感やコンテクストも伝わるので、組織の拡大によりコミュニケーションが減ってしまった部署の人同士のキャッチアップにも効果的です。 新しく入社された人が会社やチームの雰囲気を掴むのに役に立ったりします。

一方で、インターネットに公開するとなると心理的なハードルがあります。 僕は2年と10ヶ月くらいパブリックなPodcastを運営しているので、感覚が麻痺しているのですが、普通の人は自分の声がインターネットに公開されるということに不安や緊張があるみたいです。

特にエンジニアは日々ブログなどを書く人がおおく、それ以外の職種の方はそこにハードルを感じることが多いかもしれません。 そのため、社内限定、というのはその人達を巻き込むにも重要です。

僕の体験した例なので、社内コミュニケーションの活性化や関係の質の向上、というのがメインの目的になっています。 もし会社の認知拡大や広報的な側面をもたせたいなら社外公開でも良さそうですね。 公開の会社に紐づくPodcastも多く存在します。

コンテンツ

社内Podcastのコンテンツについては、会社それぞれだろ、というのが正直なところなんですが、ここでは簡単に僕がつかっていたフォーマットを紹介します。

  • 今週のニュース
  • テーマトーク
    • Onのテーマ
    • Offのテーマ

パーソナリティは僕ともうひとりの2名+ゲストの社員1名の合計3名での収録です。 現在のしがないラジオとほぼ同じ形式ですね。

今週のニュース

今週の社内であった出来事を簡単に紹介します。 誰かが入社した、こういうイベントを開催した、こういうプロダクトがリリースされた、みたいなことを話します。

いきなりゲストの方に話してもらうのではなく、話題を用意しておくことで、アイスブレイクになります。

テーマトーク

まず「OnのテーマとOffのテーマを紹介してください。」とゲストにお願いしておきます。
そして、それぞれのテーマを順に深ぼっていく、ということをします。

こう案内すると、ちょっとフックになるようなワーディングのテーマを用意してくださるので面白いです。

OnとOffというテーマも、仕事のOnとOff、気持ちのOnとOff、人間関係のOnとOffなどいろんな解釈のOnとOffがあって、意外と汎用的なフォーマットになりました。

実装方法

Podcastというのは音声メディアの1つの形ですが、Wikiによると以下のように書かれています。

ポッドキャストとは、Webサーバ上にマルチメディア・データファイル(音声データ・動画データなど)をアップロードし、RSSを通してWWW上に公開すること。その内容は、個人のブログと同様のものもあれば、テーマに基づいた対談などもあり、多岐に亘る。

つまり内容はどうあれ、インターネット上に公開された音声メディアをRSSにまとめ、公開されていれば Podcast です。 そしてこの定義が "社内" Podcast の最も高いハードルです。

RSS に IP制限 をかける、とかあんまり聞いたことないですし、Basic認証だとちょっと弱い気もしてしまいますよね。 特に上場していたりすると社内のみだから話せることもたくさんあって、そういうのの漏洩をちゃんと防いでないと運営にストップがかかったりするかもしれません。

なのでどう解決するかというと、Podcast を諦めるということになります。

多くのPodcastはエピソードの内容のサマリやリンク集、こぼれ話が書かれた*2 shownote があり、そのshownoteが掲載されたWebページがRSSとセットになっていることが多いです。

Webサイトのある音声メディアとして公開し、適切な認証をかけるのが一番良いと思いました。

僕が実現した方法は以下になります。

  • Google Driveに音源をおく
  • Kibelaに音源の共有リンクを張る
  • Kibelaにまとめ記事を作り各エピソードのリンクを張る
  • Google Formでアンケートを取る

GSuiteもKibelaももともと社内で利用していたものをそのまま利用しました。 どちらも社内であれば誰でもアクセスができるので、特別な心配は不要です。

また、KibelaはHTMLリンクを直接書けば表示されて動くので、audioタグをかけばGoogle認証しているブラウザであればKibelaで完結して音源を楽しむことができます。

まとめ記事を作って各エピソードのリンクを貼ると、各エピソードからまとめページへのリンクの自動的に入るので、簡易 Webサイトのようになります。素敵。

忘れがちですが、アンケートも取りましょう。僕はGoogleフォームを使いました。有志のプロジェクトなので動力はフィードバックです。こちらも公開用のPodcastと同じですね。

まとめ

と、ここまで書きましたが、実は僕の社内Podcast、3ヶ月くらい9エピソードくらいしか続きませんでした。 社外に公開する?しない?というのが争点となり、ちょっとややこしくなったのと、忙しくなって有耶無耶になってしまいました。

どんなことにも言えますが、走り続けていると止まりづらいですが、一旦止まってしまうとなかなか再開しづらいものです。

しかしながら、社内Podcastは最初に書いたとおり良いものですし最近の盛り上がりも感じます。 入社してすぐに勢いで始めたという経緯もあり、社内Podcastで初めて話した同僚とそれを機に仲良くなり社内のコミュニケーションハブになっていただけたのは、僕にとってもすごくやりやすかったです。

というので、皆さんも社内Podcast、やってみてはいかがですか?


しがないラジオ Advent Calendar 2019 18日目は現時点で空いていますが、meetup参加者のかたが誰か書いていただけることを祈っています。

*1:機材や音源の編集については今回は書きません。

*2:YouTubeの概要欄のような

しがないラジオを支えるオペレーション②

こんにちは、#しがないラジオ パーソナリティの zuckey です。

この記事は「#しがないラジオ Advent Calendar 2019」4日目の記事です。

adventar.org

昨日公開したしがないラジオを支えるオペレーション①の後編となっています。

blog.zuckey17.org

f:id:zuckey_17:20191204194000j:plain:w250

Podcast のオペレーションを次の4つのタイミングで分けて紹介しています。

  • 収録準備
  • 収録
  • 編集
  • 公開

後編は「編集」と「公開」について紹介します。

前編に引き続きになりますが、 Podcast を始める、続けるためのアレコレについては、僕や gami さんも寄稿している『ワンストップ Podcast』に詳しい情報があるので、気になる方はそちらも読んでいただけると良いかと思います。

しがないラジオでは音源のホスティングSoundCloud を利用していますが、その使い方や音源編集の詳細な方法などについては、この書籍に詳しく記載があるので本エントリでは省略しています。

booth.pm

そういえば、昨日のエントリに貼り付け忘れていたのですが、僕の古いエントリで収録の機材としての方法を紹介していましたので気になる方は読んでみてください。*1

blog.zuckey17.org

編集

しがないラジオの編集は基本的に僕が行っています。*2 とはいえ、これといって特殊な方法はなく音量とノイズを調整して後は余分な部分や無音部分をカットするのみです。 音源編集を調べ始めると機材も含め沼にハマりますが、しがないラジオ始まって以来僕の編集はかなり省力的に行っています。*3

Podcastの編集は何度でも推敲のできるブログとは異なり、カット=削ることしかできないので、出し続けるという目的には比較的マッチしていると、個人的には思います。

音量とノイズを調整するのは、しがないラジオにもゲストに出ていただいた FORTE さんの記事がちょうどよく詳しくわかりやすいので、参考にしていただければと思います。

しがないラジオでは、地方の方がゲストではない場合、オンサイトでの収録をしています。マイクの本数は1本で収録するので、複数人の声が1つのトラックに録音されています。 こうすることにより、収録時のセットアップ時間がかなり短縮されすぐに収録できます。 ゲストの方とは初対面のことも多く、このあたりで手間取ってしまうのが避けられているのは、スムーズな収録のために意外と重要な要素になっている気がしています。

しかしながら、この方法は音質の向上を考えると良くない方法です。 できれば1人1マイク(=1トラック)で各々の音源を細かく切り取り、クリアな音源にしたいという思いは常々あります。 また口からマイクが遠いので、その分環境音はかなりはいってしまいます。しがないラジオでは救急車やバイクの音、打鍵音がたくさん入っていると思います。*4

トレードオフですが、公開し続けることを選択するとできる限りこのあたりを楽にしておく、ということを選んでいます。

公開

編集が終わると、 音源を SoundCloud で公開します。このあたりの詳しい方法については、スクリーンショットとともに、『ワンストップPodcast』に寄稿しましたのでぜひそちらを参照いただければと思います。

Podcast というのは、Wikipedia を参考にすると次のような定義になっています。

Webサーバ上にマルチメディア・データファイル(音声データ・動画データなど)をアップロードし、RSSを通してWWW上に公開すること。

SoundCloud には音声をアップロードするだけで勝手に そのアカウント独自の RSS を作成してくれるという機能があります。そのため、SoundCloudに音源をアップロードすると誰でも Podcaster になることができます。

しがないラジオは RSS の公開だけでなく、Webサイトを用意しており、それは GitHub の GitHubPages + Jekyll の機能でホスティングしています。*5 1 Podcast エピソードあたり1 PR でマージされたらエピソードページがデプロイされるようになっています。

shiganai.org

公開は、基本的に平日朝8時くらいを目処に行っています。これにあまり意味はなく土日に出すこともありますが、僕が Podcast を通勤時間に聞くことが多いという理由から平日朝の早めの時間に公開するということにしています。

RSS と Webサイトの公開が終わるとしがないラジオの Twitter アカウントでエピソード公開した旨のツイートをします。しがないラジオのWebサイトの各エピソードページには、ツイート用のボタンがあり、「各話のタイトル」「エピソードページのリンク」「#しがないラジオ のハッシュタグ」が入るようになっているため、そこからお知らせツイートを作成します。*6

f:id:zuckey_17:20191204194401p:plain:w500

そして最後に gami さんが、収録時に撮った写真とともに 上記の公開お知らせツイートを引用リツイートする。というのがエピソード公開の流れになっています。

(色々なところが自動化できそうですが、)過去150回位はほぼこの作業で公開しています。

まとめ

編集観点で収録環境についてもかなり言及する結果になりましたが、これらがしがないラジオのオペレーションの全てです。

前編後編通して言えるのは、続けるためにできる限り手間をかけないオペレーションを選択いしていることでしょう。その分聞きづらかったりと、リスナーの方にはご迷惑をかけている部分があると思いますが、ご理解いただけると嬉しいです。 そして、これからもよろしくおねがいします。

告知

今年 2019年12月17日にしがないラジオのミートアップイベントを渋谷で開催します。半年に1回の定期*7イベントです。 今回は、有志による LT と懇親会を予定しています。12月ですし、忘年会として盛り上がりましょう!

shiganai.connpass.com

*1:当時とは変わっている部分があるかもしれません。

*2:過去数回 gami さん編集のものもあります。

*3:とはいえおまたせしてしまっている方も多いのでそのあたりは本当に申し訳ないです...

*4:いつもフィードバック頂いてありがとうございます。僕も毎エピソードちゃんと聞いており、クリティカルに聞き取れないことだけは避けているつもりです。

*5:過去、ホスティングの方法の変遷はありますが、今回は割愛します。

*6:おそらくこの機能を一番使っているのは僕です。

*7:いつの間にか定期になっていました。毎回多くの方に来ていただいて嬉しいです。

しがないラジオを支えるオペレーション①

こんにちは、#しがないラジオ パーソナリティの zuckey です。

この記事は「#しがないラジオ Advent Calendar 2019」3日目の記事です。

adventar.org

3日目にして空きが出そうだったので、業務後に急いで書いています。

f:id:zuckey_17:20191203221041j:plain:w250

しがないラジオのオペレーション

しがないラジオは、2017年3月に1エピソード目を公開してから今年の12月で約2年10ヶ月運用してきたことになります。 2年10ヶ月というと、Tech 系 Podcast の中では比較的続いている方だと思っており、そんなPodcastのオペレーションフローを紹介するのはちょっと面白いのではないかなと思って、この記事を書いております。

しがないラジオは僕ともうひとりのパーソナリティである gami さんの2人で運用していますが、オペレーションについては特に明文化されているものはなく2人とも雰囲気で動いています。そのため、こちらに書かれているものは実績ベースで「こうしていることがおおいよ」というものを書いている、ということをエクスキューズしておきます。

ここから先は Podcast のオペレーションを4つのタイミングで分けて紹介します。 本エントリは①前編になっており収録準備と収録についてを書きます。②後編では編集および公開の項目について書きます。

  • 収録準備
  • 収録
  • 編集
  • 公開

また、 Podcast を始める、続けるためのアレコレについては、僕や gami さんも寄稿している『ワンストップ Podcast』に詳しい情報があるので、気になる方はそちらも読んでいただけると良いかと思います。 しがないラジオでは音源のホスティングSoundCloud を利用していますが、その使い方や音源編集の詳細な方法などについては、この書籍に詳しく記載があるので本エントリでは省略します。

booth.pm

収録準備

収録準備でやることは「ゲストアサイン」「場所と日付の決定」「収録時メモの準備」です。

しがないラジオは初期の頃パーソナリティ2人で収録し公開していましたが、ある時期からゲストを呼んでその方のキャリアやその考え方、気になる技術、趣味などを聞くエピソードが増えました。 ゲストアサインのルートはパーソナリティの知り合いつての紹介や SNS(Twitter) での自薦が多いです。直接 Twitter DM を送ってきてくださった方も数人いらっしゃいます。 *1

基本的には、ゲストアサインから収録日までのやり取りは Twitter DM にて行います。 しがないラジオでは、収録時のメモを Dropbox Paper にて管理しており、場所と日付を決めてから、用意してあるテンプレートをゲストの方に送付します。 テンプレートの中身についてについては後述しますが、「話したいことメモ」、「収録前TODO」、「収録後TODO」の項目があり、「話したいことメモ」の項目についてゲストの方に事前に書いていただいています。

ゲストの方に書いていただいた「話したいことメモ」は当日の直前までできる限り読まないようにしています。 ギリギリまで編集されることがほとんどですし、当日に読んだほうが新鮮さがある状態で会話ができるためです。*2

収録

収録は原則収録時のメモの通りにすすめます。ゲストの方に送るメモは次のようになっています。

# sp.xx ○○ さんゲスト回

話したいことのメモがあれば、事前に箇条書き等で記載していただけると助かります!

なんとなくメインテーマみたいなものがあると、聴く方も聴きやすいかなーと思います。
1〜1.5hで1配信なので、長い場合は2配信に分けることもあります!

よくあるネタとしては、以下です。

- これまでのキャリアの話
- 今やっていることの話
    - 仕事
    - 趣味
- 深掘りして話したいトピック
    - 技術
    - コミュニティ
    - 転職・キャリア


## 当日TODO(収録前)

- [ ] 呼び方確認
- [ ] 収録終了時間
- [ ] レギュレーション
- [ ] 収録の流れ
    - [ ] タイトルコールは別録りです
- [ ] 告知内容


## 当日TODO(収録後)
- [ ] タイトル決め
- [ ] 公開前チェックの有無確認
- [ ] タイトルコール録音
- [ ] 写真撮影

収録前TODO

まず呼び方を確認します。ゲストの方は、 SNS でつながっていたとしても収録当日に初めてお会いする方も多いです。そのため、収録が始まってから呼び方勘違いしていることに気づく、ということがあってからこの項目は追加されました。*3

次に収録終了時間の確認です。しがないラジオは比較的エピソードの時間が長く、その分収録時間も長い*4ため後の予定を事前に確認しておきます。

レギュレーションというのは「言及してはいけないこと」です。本名や過去、現在の勤務先、その他の情報で言及したくないことはここでうかがっておきます。後からカットすることもできますが、パーソナリティが深堀りする方向を調整することもできるので事前に聞いておきます。

「話したいことメモ」をみながらざっくりとしたエピソードの構成を決めます。しがないラジオのゲスト回は多くの場合1回の収録の音源が前後編に分けられるので、どのあたりで区切るのか、というのをざっくり決めておきます。初めて出演されるゲストの方は、キャリアや現職のことを前編に話していただき、後編でより深堀りしたいトピックを持ってくることが多いです。

最後に、告知物を確認しておきます。収録の最後に告知タイミングを用意しているので、そちらで告知したいものの詳細を事前にうかがっておきます。事前に聞いておくことで、エピソードのなかで絡めて話すこともできます。

収録後TODO

収録が終わった後に、エピソードのタイトルの方向性を決めます。タイトル付けによってそのエピソードが聞かれる数が変わってくることはわかっているので「どの話題がリスナーに刺さりやすそうか」「過去のゲストと比べてユニークな点はどこか」というところを中心にワードを洗い出し、gami さんが後から良い感じにタイトルにしてくれます。

また、収録後に公開前の音源チェックの確認もしています。概ね公開前チェックはない場合が多いですが、かための企業に所属されていたり、喋ったことが心配になったりすると、編集後の音源を聞いていただくことも可能ということにしています。

しがないラジオの各エピソードは、「しがないラジオ〜〜〜♫」というのをパーソナリティとゲストで叫ぶというタイトルコールから始まります。よく驚かれるのですがこれは別撮りです。 僕らも始めは恥ずかしがられるかな?と思っていたのですが、意外とみんなノリノリでやってくださいます。

最後に、告知などに利用するための写真を撮影します。写真撮影といってもめちゃくちゃ雑で、gamiさんの長いリーチを利用した Pixel3 のインカメラによる自撮りです。

まとめ

しがないラジオの収録準備と収録のオペレーションフローについて紹介しました。Podcastを長く続けるために、場所と日付を早めに決め、収録前にやることを極力少なくしています。収録してしまえば(編集に時間はかかりますが)出さない、という選択肢はなくなります。これまでお蔵入りになったエピソードはないです。

現在、年内の収録予定はありませんが(編集が終わっていないエピソードがあるため)年明けからはまた収録していきたいと思っているので、ぜひ Twitter などで出たい、とメンションしていただけると、嬉しいです。

それでは、後編で!!

blog.zuckey17.org

*1:「しがないラジオ出たいボタン」というのがありまして、Twitter上で時々押していただいていますが、こちらについてはパーソナリティ2人の合意でこのボタンを叩いてツイートされただけではこちらからお声がけしないことになっています。 https://btnmaker.me/b/642b6050-b4a9-11e8-a7d3-fd082b7ee458

*2:収録準備の工数を最小化することで、運用コストも抑えられます。

*3:ゲストの方がぼくのことを「ざっきー」だと思っていることも少なくないのでここでインプットしておきます笑

*4:およそ3~4時間です