エンジニア組織が大きくなるにつれて参考にしてきた書籍

事業開発チームの嶋田です。前回のブログでアジャイルな開発手法を取り入れて約一年と最初に書きましたが、それはエンジニアが組織化するようになったのとほぼイコールになります。

今回はそこを振り返るとともに都度参考にしていた書籍を紹介していきたいと思います。

スクラムの導入

SCRUM BOOT CAMP THE BOOK

スクラムの導入に当たりWebエンジニア全員でこの書籍を読んで導入をすすめていきました。

平易で読みやすくスクラムの概要が頭に入った形で進めたことや、スクラム経験者のサポートもありスムーズに始めることができたかと思います。

日々スクラムを維持していく上で課題は出ていますが、前回のブログでも書いたように振り返りも機能したりしているので、1年前と比べるとだいぶ洗練されたチームになってきたのかなと感じています。

採用について

作るもの・作る人・作り方から学ぶ 採用・人事担当者のためのITエンジニアリングの基本がわかる本

エンジニア組織として大きくしていく関係で採用にも力を入れています。採用に力を入れていくにあたって、個人的にはまずエンジニアの職種ってなにがあるのかとか自分が採用するにあたって足りない知識があるのだろうかということを知りたくこちらの本を参考にしました。

エンジニアをマネージメントするにあたって

HIGH OUTPUT MANAGEMENT

エンジニアのためのマネジメントキャリアパス ―テックリードからCTOまでマネジメントスキル向上ガイド

私自身はマネージメントをする時間の割合も多くなりました。組織人として求められる役割のひとつに視座を自分のポジションよりひとつ上に持つみたいなことをよく言われるかと思います。

そういった手がかりになる一つかこれらの書籍にあるかと思います。読んだのは半年以上前ですがそろそろまた読み返しても新たな発見がありそうです。

ファシリテーターとして

世界で一番やさしい会議の教科書

ファシリテーターをする機会も多くなり、自分がうまくファシリテーションできないこともありました。

そのときに紹介された書籍です。この本が恐ろしいのはたぶん会議に興味がなくても読み進められるくらいストーリーが面白いです。

こちらは入門書ですし、エンジニアリングとは対極にありそうなスキルなファシリテーションは組織の生産性に直結しそうな課題でもあるので引き続き書籍を読んでいきたいと思っています。

最後にビジネス書を読むのにおすすめな方法

ここにあげられた多くの書籍は紹介されて読んでみたものなのですが、タイトルを検索した時に気づいたんです。結構Kindle Unlimitedにあるじゃないか!と。

漫画読みである自分は何回かKindle Unlimitedを利用したりしたんですが、漫画に関してはいいものが少なく、Kindle Unlimitedはもっぱら雑誌読みに利用することが多かったんですが、ビジネス書においては良書がたくさんあるんだなと気付かされました。

また、この本の2章だけしか役に立たなかったなと感じる書籍もあるかと思いますが、Kindle Unlimitedであればそういった買って失敗した感がなくなるのもよいです。

ビジネスパーソンな皆様、ぜひ読み放題なKindle Unlimitedを利用しましょう。

 
 

スプリントレトロスペクティブを実施するようになって目に見えて変わったところ2選

事業開発チームの嶋田です。弊社はアジャイルな開発手法を取り入れてまもなく一年になろうとしています。

私自身はアジャイル導入前の状態もよく知っているため、導入前後で大きく変わったところも見てきました。

アジャイルは開発手法として一般的なスクラムを弊社も行っていますが、スクラムの活動の中でもスプリントレトロスペクティブ(長いので以下振り返り)をするようになったことが、エンジニアの意識も大きく変わったところじゃないかと感じています。

今回はその中でチームとして大きく変わったというところを2点上げていきたいと思います。

不具合発見の増加

不具合検知の仕組みがまだまだなところがありますが、最近整備できたことのひとつにHTTPステータスコードの監視があります。

これまでのリリース後の不具合発見の多くは非エンジニアチームの報告が多かったのですが、これによりエンジニア発の不具合報告も数として多くなってきました。

ステータスコードの監視は仕組みとしては少し前からあったのですが、毎日みんなで確認することを振り返りですることが決まり、これにより全員の正常動作に関する意識が高まりました。

少し違和感がある状態があると調査を行い結果的にその周辺でバグを発見したりというケースも出てきて、良い循環が生まれています。

曖昧さの排除

今回ブログを書くにあたって振り返りのログを一通り見たんですが、振り返りがうまく働くとそのチームの問題点が浮き彫りになるのかなとログをみて感じ、自分たちの問題点は曖昧な部分が多かったというのが気づきでした。

スクラムの導入前まではそれぞれのエンジニアが自分たちの力量で細かなところをカバーして運用していくという割合がおそらく他社比でもかなり多かったかと思います。

例えば、ビジネスサイドからのタスクの依頼方法はテンプレートに沿って作ってもらいこれを運用していく形になっていました。

これまでだとこのテンプレートの方法だととあるケースでは分かりづらいなとかあったとしても、報告の場があまりなかったこともあり、その時にエンジニアが追加でヒアリングするなどして対応するみたいなエンジニアがその場その場で臨機応変に対応するといったことがこのケースに限らず多発していました。

しかし振り返りが習慣化したことで、これはあとで振り返りで議題として上げようといった流れができて、曖昧なところがどんどんと解消されていっています。

これによりあとからエンジニアが参加してもキャッチアップの時間が短くなり、より組織としてスケールしやすい体制になりつつあるかと思います。

 

 

以上、1年近くスプリントレトロスペクティブを運用しての気づきでした。引き続き組織としてブラッシュアップすべく改善を繰り返していきたいと思います。

 
 

モブプロをはじめました

1. はじめに

カラダノートの 堀内 です。

開発チームでは最近モブプロ(モブプログラミング)をはじめました。 その経緯や生の感想などを書こうと思います。モブプロの説明は端折ります。

なぜモブプロを始めたか?

当社ではエンジニアの採用を強化しており、ありがたいことに続々とエンジニアの入社が増えています。 1週間スプリントをベースにPDCAを回していることもあって、チームに入れば業務知識や既存のシステムのキャッチアップなどはうまく回っている実感があります。

一方で、技術面では、「技術のノウハウ共有」に課題を感じていました。

具体的に言うと、

「業務知識面では誰が何に詳しいかの相互理解が進んでいる」けど

「技術知識面では誰が何に詳しいかの相互理解が進んでいない」

という状態です。

そこでモブプロを導入し、「ノウハウの共有」および「この技術はこの人が詳しいという相互理解」を進めようと考えました。 特に後者は、「誰が何に詳しいか」がわかっていれば「詳しい人に質問して答えにたどり着くスピードが上がる」点でより重要だと思っています。

どうやってはじめたか?

「モブプログラミング・ベストプラクティス ソフトウェアの品質と生産性をチームで高める」を参考に、まずはきっちり型通りにやるようにしました。

www.amazon.co.jp

この本がとても参考になるのは、モブプロにもきちんと型、というか 役割とルール がきっちり明確になっている点です。

つまり、モブプロには タイピストモブ という役割があり、それぞれ どのようなことが求められているか が明確に記載されています。

他にも、取り組むテーマの設定の仕方、モビングセッションとインターバルのタイムテーブルの設定、タイピストの交代ルールなどなど...、とにかく細かいところまで役割とルールが言及されており、この本の通りにやればグダグタにならずに取り組める点がとても良いです。

特に初回の開催で重視したルールは以下の3つです。

  • タイピストは普段自分がやらないやり方でやる
  • タイピストはあくまでタイピングに集中する
  • モビングインターバルは厳格に守る

そのうえで、作業を開始する前に「このモビングセッションのゴール」を明確に設定しました。 本のとおりまずは4人でやってみました。

やってみた結果

前提としてメンバーが前向きに取り組んでくれたこともあり、初回のモビングセッションの振り返りで以下の手応えを感じました。

  • 「メンバー各々色んなやり方があってそれぞれにメリット・デメリットがある」ということ
  • そしてそれらをメンバー間で共有できたこと

例えば、「開発サーバーののログを確認する」という作業一つとっても、様々なやり方があります。

  • サーバーに入って直接調べる派
  • ローカルに落として調べる派

など。 そしてそれらにはメリットとデメリットがあり、内容を精査すれば やり方を標準化できる という次の課題が見えてきます。 やり方を標準化できれば、オンボーディングにも役立ちますし、単純に作業効率と品質が安定し、情報共有の際の認識の違いも生まれにくくなります。

また、当初の目的である「この技術(やり方)はこの人が詳しいという相互理解」についても、振り返りでの議論を通して醸成できると感じます。特にツールの使い方などはその典型です。これは「普段自分がやらないやり方でやる」の恩恵でもあります。

最後に

モブプロ、本当に書籍どおりにやってみただけですが、とても大きな手応えを感じました。 これも書籍どおり、 まずは5回やる を目標に取り組んでいこうと思います。

他にも細かい気づきはたくさんあったのですが、それはまた別の記事で書こうと思います。

それでは。

PrismaからRDBのViewを参照する

1. はじめに

カラダノートの 堀内 です。 TypeScriptの次世代ORMである Prisma から、RDB側で作成したViewを参照する要件がありました。

せっかくなのでその手順をチュートリアルにします。

2. Viewを参照するチュートリアル

公式にPostgreSQLを例にしたドキュメントがあるので、そちらを参考にします。

www.prisma.io

RDB側の準備

まず RDB側に Viewを作成します。

公式のクイックスタート からサンプルプロジェクトをダウンロードします。

curl -L https://pris.ly/quickstart | tar -xz --strip=2 quickstart-master/typescript/starter

サンプルプロジェクトのprisma/dev.dbSQLiteのDBが以下の構造で同梱されています。

f:id:karadanote:20210416112450p:plain

PostとUserはPost.autherIdとUser.idでリレーションが構成されています。この2つのテーブルを結合した以下のようなViewを作成してみます。

f:id:karadanote:20210416112728p:plain

以下のコマンドからViewを作成します。

sqlite3 dev.db
CREATE VIEW view_posts AS SELECT Post.id, Post.title, Post.content, Post.published, Post.authorId, User.email, User.name from Post LEFT JOIN User on Post.authorId = User.id;

Viewが作成されたか確認してみます。

f:id:karadanote:20210416112825p:plain

Prisma側の準備

サンプルプロジェクトのprisma/schema.prismaに↑で作成したViewのモデル定義を記述します。

通常のテーブルであれば、npx prisma introspectRDBから自動的に検出してschema.prismaに記載してくれるのですが、現時点では以下の公式ドキュメントの記述どおり、Viewのintrospectionはサポートされていません。

You must manually add each view as a model to the Prisma schema right now. Introspection does not add views to the schema currently.

なので、自分で書きます。

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model Post {
  id        Int     @id @default(autoincrement())
  title     String
  content   String?
  published Boolean @default(false)
  author    User?   @relation(fields: [authorId], references: [id])
  authorId  Int?
}

model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
  posts Post[]
}

+ model view_posts {
+   id        Int     @unique
+   title     String
+   content   String?
+   published Boolean @default(false)
+   authorId  Int?
+   email String
+   name  String?
+ }

ここで2つポイントがあります。

  • モデル名はView名と同じにする
    • 同じにしないと does not exist in the current database. がでます
  • 何らかのカラムが必ずユニークになるようにする
    • @idや@uniqueを最低でも1つ指定します

コードからViewを呼ぶ

サンプルプロジェクトの script.ts から↑で作成したモデル定義を使ってViewを読んでみます。

import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
  const posts = await prisma.view_posts.findMany()
  console.log(posts)
}

main()
  .catch(e => {
    throw e
  })
  .finally(async () => {
    await prisma.$disconnect()
  })

↑のコードを実行してみると、Viewのデータを参照できていることが確認できます。

eiichihoriuchi@EiichinoMacBook-Pro starter % npm run dev        

> script@ dev /Users/eiichihoriuchi/workspace/starter2/starter
> ts-node ./script.ts

[
  {
    id: 1,
    title: 'Hello World',
    content: null,
    published: false,
    authorId: 2,
    email: 'maria@prisma.io',
    name: 'Maria'
  }
]

3. おわりに

Viewを活用してコードから参照できれば、コードの中に複雑なSQLやデータの整形を書かずに済むので見通しが良くなりますよね。 GraphQLでもいいんですが、サブクエリや集計を利用するケースではRDBにもメリットがありますね。

TypeScript で 都道府県型を定義する

1. はじめに

カラダノートの 堀内 です。 前回 は電話番号型を定義してので、今回は都道府県を実装してみます。

2. ゴール

都道府県型を使って何を実現したいかというと、例えば、北海道と沖縄だけを対象とする処理を実装するときに、それ以外の都道府県だと型チェックでエラーを出したい、ということを目指します。

以下のようなイメージです。 ちなみに Prefecture<N> の Nは 都道府県コード に相当します。

  const hokkaido = Prefecture.from(1)   // Prefecture<1>
  const okinawa = Prefecture.from(47)  // Prefecture<47>
  const saitama = Prefecture.from(11)  // Prefecture<11>

  // 北海道と沖縄のみを対象とする処理
  const sendTo = (prefecture: Prefecture<1> | Prefecture<47>): void => {
    // do something ...
  }

  sendTo(hokkaido)
  sendTo(okinawa)
  sendTo(saitama)  // ここを型チェックでエラーを出したい

3. 実装

もういきなりですが以下のとおり実装しました。

/* prefecture.ts */

// 都道府県コード一覧
const PREFECTURE_CODES = [
  1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
  11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
  21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
  31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
  41, 42, 43, 44, 45, 46, 47
] as const

// 都道府県名一覧
const PREFECTURE_NAMES = [
  '北海道', '青森県', '岩手県', '宮城県', '秋田県', '山形県', '福島県', '茨城県', '栃木県', '群馬県',
  '埼玉県', '千葉県', '東京都', '神奈川県', '新潟県', '富山県', '石川県', '福井県', '山梨県', '長野県',
  '岐阜県', '静岡県', '愛知県', '三重県', '滋賀県', '京都府', '大阪府', '兵庫県', '奈良県', '和歌山県',
  '鳥取県', '島根県', '岡山県', '広島県', '山口県', '徳島県', '香川県', '愛媛県', '高知県', '福岡県',
  '佐賀県', '長崎県', '熊本県', '大分県', '宮崎県', '鹿児島県', '沖縄県'
] as const

// 都道府県コードのUnion型
export type PrefectureCode = typeof PREFECTURE_CODES[number]

// 都道府県名のUnion型
export type PrefectureName = typeof PREFECTURE_NAMES[number]

// 都道府県コードの検証
const validate = (value: PrefectureCode): boolean => {
  return PREFECTURE_CODES.includes(value)
}

// 都道府県型
export type Prefecture<Code extends PrefectureCode> = {
  readonly code: Code
  readonly name: PrefectureName
}

// 都道府県型の生成
export const Prefecture = {
  from<T extends PrefectureCode>(value: T): Prefecture<T> {
    if (!validate(value)) {
      throw new Error(`${value}は都道府県コードではありません`)
    }

    return {
      code: value, 
      name: PREFECTURE_NAMES[value - 1]
    }
  }
}

export default Prefecture

で、ゴールのところでで書いたコードで動きを確認してみると、埼玉が型チェックでエラーになっています。

f:id:karadanote:20210405131545p:plain

ドメイン知識をこんな感じで型定義にどんどん集めていきたい。 的なドメイン駆動設計をやりたい方をカラダノートでは募集しております。

herp.careers

herp.careers

TypeScript と Opaque型で電話番号型を定義する

1. はじめに

カラダノートの 堀内 です。

TypeScript の型定義ライブラリの1つである type-fest を使って、電話番号型 を作って実験したメモです。

github.com

2. Opaque型について

2.1 実装の種類

TypeScriptでは公式にOpaque型をサポートしていないので、色々ググってみると現状以下の呼び方と実装があるようです。

  • Nominal型(公称型)
  • Opaque型(不透明型)

今回は type-festのOpaque型を使いましたが、utility-types ライブラリのNominal型でも同じことが実現できます。

というわけでOpaque型で実装してみます。

2.2 電話番号型の実装

今回は以下のように実装しました。

import { Opaque } from 'type-fest'

// 電話番号になりうる基底型
type BaseType = string

// 電話番号型
type PhoneNumber = Opaque<BaseType, 'PhoneNumber'>

// 電話番号型になりうるかの検証
const validate = (value: BaseType): value is PhoneNumber => {
  // 半角数字1文字以上を電話番号とみなす
  return /^[0-9]+$/.test(value)
}

interface CreateValueObject<Base, T> {
  (value: Base): T
}

const PhoneNumber: CreateValueObject<BaseType, PhoneNumber> = (value) => {
  if (!validate(value)) {
    throw new Error(`${value}は電話番号ではありません`)
  }
  return value  // as PhoneNumber としなくても PhoneNumber型が返る
}

export default PhoneNumber

2.3 実装した電話番号型の使い方

以下のように使用します。

import PhoneNumber from './phoneNumber'

const phoneNumberA = PhoneNumber('0120123456')
const phoneNumberB = PhoneNumber('08098765432')

console.log(phoneNumberA == phoneNumberB)
// false

2.4 Classでの電話番号型の実装

同じことはClassを使っても実現できます。例えば以下のように実装します。

export default class PhoneNumberClass {
  constructor(readonly value: string) {
    if (!this.validate(value)) {
      throw new Error(`${value}は電話番号ではありません`)
    }
  }

  validate(value: string) {
    // 半角数字1文字以上を電話番号とみなす
    return /^[0-9]+$/.test(value)
  }

  equals(other: PhoneNumberClass) {
    return this.value == other.value
  }
}

3. Opaqueの電話番号型とClassの電話番号型の違い

ここからは2つの実装の違いについて、色々実験してみます。

3.1 JSON文字列への変換

たとえば以下のようにuserAとuserBを作ってJSON文字列にしてみます。

  • userA: Opaqueで実装した電話番号を使用
  • userB: Classで実装した電話番号を使用
const userA = {
  name: 'karada',
  phoneNumber: PhoneNumber('0123456789')
}

const userB = {
  name: 'note',
  phoneNumber: new PhoneNumberClass('0123456789')
}

console.log(JSON.stringify(userA))
console.log(JSON.stringify(userB))
{"name":"karada","phoneNumber":"0123456789"}
{"name":"note","phoneNumber":{"value":"0123456789"}}

上記のとおり、Classで実装したほうはそのまま使うと{ "value": "xxx" } になってしまいます。

3.2 Jsonオブジェクト型への設定

以下のようなJSONオブジェクト型を定義してプロパティに電話番号を設定してみます。

type Json =
  | string
  | number
  | boolean
  | null
  | { [property: string]: Json }
  | Json[]

const userA: Json = {
  name: 'karada',
  phoneNumber: PhoneNumber('0123456789')
}

const userB: Json = {
   name: 'note',
   phoneNumber: new PhoneNumberClass('0123456789')
}

すると、userBのほうは型チェックでコケます。Json型はプリミティブのみサポートする型定義をしているので。

f:id:karadanote:20210331085256p:plain

3.3 ORMを使ったDBへの登録

同様に以下のような schema.prisma で定義されているDBに対して、Prisma Client を使ってデータを登録してみます。

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model User {
  id    Int     @id @default(autoincrement())
  name  String?
  phoneNumber   String?
}

f:id:karadanote:20210331085415p:plain

このケースも同様にコケます。Prismaが生成するテーブルのカラムの型定義もプリミティブになっているので。

まとめると

Opaqueで実装した電話番号型では、JSON化したりDBに登録したりするサービスを実装するときに、プリミティブ型と同じように扱える、ということです。

もちろん、そもそもの Opaque型の利点としては、PhoneNumberを単にstringのエイリアスと定義しないことで、以下のような型チェックの恩恵を受けられることにあります。

f:id:karadanote:20210331001651p:plain

f:id:karadanote:20210331001702p:plain

他にもOpaqueでできることがあれば試してみたいですね。

Appendix

他の実装パターンとして以下の実装も参考にしました。

また、参考図書として、こちらの4章 ~ 6章が特に参考になります。

www.oreilly.co.jp

Prisma Client で踏み台のEC2を経由してRDSに接続する

はじめに

カラダノートの堀内です。 最近弊社では TypeScript ✕ Prisma の導入を進めはじめました。

この記事は Prismaのintrospection を開発環境やステージング環境のDBに対して踏み台のEC2を経由して実行するときのメモです。

f:id:karadanote:20210329000741p:plain
やりたいこと

手順

1) まずSSH でトンネリングしておきます。

$ ssh -N -L 3307:rdsのホスト:3306 ec2のユーザ名@ec2のホスト -i ec2のpem

2) prismaの設定を以下のように変更しておきます。

prisma/schema.prisma

datasource db {
  provider = "mysql"
  ...

prisma/.env

DATABASE_URL="mysql://DBユーザ名:パスワード@127.0.01:3307/接続先のDB名"

3)これで introspection を実行すれば接続先のRDSのDBから schema.prisma を生成してくれます。

$ npx prisma introspect

参考元

本家リポジトリに同様のIssueが挙がっていたので参考にしました。

Database Connection Via SSH Tunneling