The Design of Web APIsの読書感想文(7章まで)

www.manning.com

The Design of Web APIsは我流でAPI設計をやってしまっていた自分にとって良書でした。 知らなかった事・気がついた事等を忘れないように箇条書きにしました。 7章以降も少しずつ読んでいこうと思います。

ch1: what is api design?

  • APIの基礎的な部分を説明した章
  • 酷いドローンのコントローラ設計を題材にして話が進んでいく
  • public apiの場合、コロコロ使い勝手を変えることが出来ないし収益に関わる部分だから、設計がより一層シビアになる
  • RPC, SOAP, REST, gRPC, GraphQL等いろいろな通信のための手段があるが、そこだけを勉強しても良いAPI設計を作ることが出来ない
  • APIを使う側のユーザが達成したいゴールを意識することが大事
  • どのように使われるのか、誰が使うのかといったユースケース的な情報が大事になってくる

ch2: Designing an api for its users

  • ずっと押しっぱなしにしとかないと動かない酷い設計の電子レンジとショッピングサイトのwebAPIが題材になっている
  • ユーザー目線を持つことが大事
  • 内部の仕様を漏らすようなことをしない。ユーザーが知らなくていい部分には蓋をして見えなくする
  • APIの実装漏れや分かりやすさを追求するために「API goals canvas」を作成する
  • API goals canvas: Whos, Whats, Hows, Inputs, Output, Goalに着目する
  • コンウェイの法則(組織内部でとっているコミュニケーションがシステム設計に現れる法則)
  • ビジネスロジックアーキテクチャ、DBの名前等がAPIに漏れ出していたら注意。※ ユーザのゴールに着目すること

画像は自分で作ってみた動物園APIAPI goals canvas

ch3: Designing a programming interface

  • RESTの説明から入る
  • ch2で作ったAPI goals canvasをもとにRESTに書き起こしていく
  • リソースの関係性を洗い出すことが大事。has a, has many
  • goalを書いてその動詞に注目する。add, replace, create, delete等
  • addとsearchの関連性に注目
  • 例えば、productにitemを追加する。productからitemが検索される。productとitemを親子関係で表現できる
  • itemを作ったらIDを返却する
  • PATCHは部分更新
  • PUTは更新だけでなく存在しない場合に作成しにかかる。save的な感じ。
  • DELETEは更新をかけてユーザから見えなくする"ソフトデリート"でもDELETEを使ってOK
  • ユーザーが達成したいゴールを見直してパラメータを削っていく
  • HTTP メソッドにマッピングできなかった場合 => アクションリソース(checkoutみたいな)
  • 名詞ではなくcheckoutのような動詞混ざりのエンドポイントの場合RESTで表現するのが難しくなる
  • 無理やりRESTに全てハメこむのはユーザフレンドリーではない。アクションリソースで整えるのも大事。
  • RESTの原則と可読性のバランスを取ることが大事。

ch4: Describing an API with an API description format

  • OASを紹介する章
  • スプレッドシートを使うよりOASを使った方が他のライブラリを適用できるため恩恵がある
  • swagger以外にもReDocなどOASファイルを表示するUIに関しては複数ある
  • 以降記述の仕方等は前職で使っていたので流し読み
  • 次の職場で使う時に以下のopenapi-mapに目を通しておいた方がよさそう

openapi-map.apihandyman.io

ch5: Designing a straightforward API

  • 使いやすく・分かりやすいAPIを定義するためにどうすればいいのかを考える章
  • バックエンド側でenumを変換してnameに変換する(前職ではフロントで実施してしまっていた。フロントの責務が増えるので確かに微妙だった)
  • フォーマット関連の処理はMUIのDataGrid等のライブラリで提供されてはいるもののバック側でやってしまった方がいいかも。(SPA構造にする時だけ使うものだった?)
  • この章ではエラーを3つに分離(Surface: 400, Business: 403, InnerControl and Unexpected: 500)
  • 前職では、SurfaceとBusinessを同じステータスで返却してしまっていた
  • 分かりやすいエラーメッセージを返却するために要素ごとに情報を作成する
{
  message: XXX,
  errors: [
    {
      要素名: A,
      エラー種別: XXX,
      メッセージ: XXX
    },
    {
      要素名: B,
      エラー種別: XXX,
      メッセージ: XXX
    },
  ]
}
  • 成功ステータスは200以外にも201と202が存在する
  • 作成した時は201を返却する
  • 遅延処理が発生するかもしれない処理が成功した事に対して202を使用する
  • 201の場合、すぐに完了したことを表現し202の場合、まだ処理中のことを表現する(送金中とかメール送信中とか)
  • 前職では全て200で返却してしまっていた
  • 遅延処理の場合、HTTPステータスだけでは不十分なので別途通知を送るなど対応が必要なので注意

ch6: Designing a predictable API

  • 5章に引き続きさらにAPIを使いやすくする方法が掲載されている
  • 命名規則の統一(createdAt,DateTimeOfUpdateなどごちゃつかせない)
  • Formatの統一(色々な時間フォーマットで返さない)
  • データ構造の統一
  • content negotiationに包めるものは、parameterに入れない

developer.mozilla.org

  • ページネーションにRangeヘッダーを使う例も乗っていたがバイナリーファイルを扱うのが目的なのでpageSizeとpageで管理した方がよさそう

developer.mozilla.org

  • ソートパラメータの指定方法の一例: sort=-amount,+date
  • meta_data: paginationに必要なデータやアクションが実行可能なことを表現するデータ。返却するデータの本質とは関係ない。操作に関連する物。
  • hypermedia APIs: レスポンスの中にurlが入っているAPI
  • URLを事前に知らなくても情報に辿り着ける。利用者(フロント側)でURLを定義しないくていいので改修がスムーズになる。
{
  _links:{
    self:{ 
      href: "url",
    },
  
    next: {
      href: "URL"
    },
    prev: {
      href: "URL"
    }
  },

  properties:{
    ...
  }
}

stateless.co

ch7: Designing a concise and well-organized API

  • データ構造のまとめ方について紹介する章
  • ネストさせてデータをグループ化させ可読性を向上
{
  memberName: xxxx,
  school:{
    name: xxxx,
    address: xxxx
  }
}
  • 重要なプロパティを上にする
  • OAS: タグ付けしてAPIをグループ化
  • 入力は可能な限り少なくネストも少なくする(推奨3段階まで)
  • 出力のネストも可能なら三段階までを推奨
  • Avoid creating does-it-all goals(何でも出来るゴールの設定をしない)

「Rubyで実装するフルスクラッチ三目並べ」という名前でgitbook書いてみました

1 ~ 2年前に研修用に作ってWEB公開するのを忘れていたので、さっき公開してきました。
今見ると結構荒削りな所がある(変数名とか内容とか)ので気が向いたら直していこうと思います。

kashiwara.gitbook.io

対象は誰なのか

  • プログラミングに興味があって基礎構文をやった方
  • dockerを使って開発してみたい方
  • 簡単なゲームを作ってみたい方
  • 単体テストを実装してみたい方 などなど

何で作ったのか

以下のような悩みを持つ人が前職場に多かったからです

  • 社内にプログラミングのチームに入りたいけど何から勉強したらいいか分からない
  • プログラミングスクールに通っていて成果物が出来上がってるのに、何故か現場のコードが読めない

元々僕がdiscord上で教えていましたが、誰でもトライできるように本にしました。

まとめ

本の途中で静的解析ツールを入れてコードをフォーマットさせるのですが フォーマットさせる前とさせた後のコードを管理しなければならず結構大変でした。 本を書くのって結構体力がいるのだなと思いました。

HaskellでLeetCodeのTwoSumを解いてみた

転職活動を終えて時間に余裕が出てきたので、最近Haskellで遊んでいます。
LeetCodeのTwoSumをHaskellで解いてみました。

TwoSum

今回トライした問題

leetcode.com

レポジトリ

Haskell勉強用に作ったレポジトリです。
docker-compose upで環境が立ち上がりテストコードを実行することができます。

github.com

回答

リスト内包表記を使ってみました。
遅延評価使ってるのとzipでindex値を出すところがミソです。

module TwoSum where
    pickIndex' :: [(Int, Int)] -> [Int]
    pickIndex' [] = []  
    pickIndex' (x:xs) = [fst x, snd x]

    twoSum :: [Int] -> Int -> [Int]
    twoSum xs num = pickIndex' [ (i, j) | (x, i) <- zip xs [0..], (y, j) <- zip xs [0..], x + y == num, i /= j]

テストコード

TwoSumのテストコードです。

import Test.HUnit
import TwoSum

testTwoSum :: Test
testTwoSum = "twoSumを検証する" ~: test
    [ "3の合計値を算出するindexは [0, 1]" ~: twoSum [2,7,11,15]  9 ~?= [0, 1],
      "6の合計値を算出するindexは [1, 2]" ~: twoSum [3,2,4]  6 ~?= [1, 2],
      "6の合計値を算出するindexは [0, 1]" ~: twoSum [3,3] 6 ~?= [0, 1]
    ]

main :: IO ()
main = do
    runTestTT (TestList [testTwoSum])
    return ()

Haskellは3~4年前に一回触ったきりだったのですが 久しぶりに触ると新鮮で楽しいです。

再帰の組み方とか勉強になります。

プロダクトマネージャーのしごとを読んだ

背景

先日リファレンスチェックを実施していただき自分の弱みに触れる時間があった。
どうも自分は技術思考にかなり寄っているらしく 緊急対応然り何かトラブルが発生した場合に、人とコミュニケーションを取って解決するのではなく、技術で突破しようとする癖があるらしい。
そういったフィードバックをいただいた訳だが、改めて自分のやり方を考えてみると確かに当てはまった。 納期に間に合わなさそうとなると設計を変更して工数を短縮するし、フロントの描画が遅いとなるとコンポーネントの最適化に走る。
そこで、技術以外の解決方法という思考を身につけるため、手に取った本がプロダクトマネージャーのしごとだった。

www.oreilly.co.jp

そもそもなんで技術寄りになってしまうのか

理由は非常に単純でだから。
コミュニケーションをとって折半案を出すというのは、対人になる。
特にリモートだと非同期コミュニケーションが常に発生する為より難易度が向上する。
対して技術でどうにかするというのは、自分との同期コミュニケーションになるので、凄く楽。
ただ、それが本当にプロダクトに良い影響を与えてるとは限らない。
悪い影響を与える方が大きい可能性があるということを本書を読んで気付かされた。

アウトカムを意識する

アウトカムのためのアウトプットというキーワードが本書に登場するが、まずこれが出来ていなかった。
役員会で機能と納期が決定されており、それを突破するためにひたすら実装するというスタイルでやってきた。
役員レベルで決まっていることになるので基本的に意見を覆すことは絶対にできない。
そこにはアウトカムを意識した開発はなく、よくわからないけど依頼されたから実装するというスタイルに陥っていた。
無駄になった機能の数は計り知れない。
この経験からプロダクトオーナーのような現場と上層部の認識をすり合わせる役目が必要なのだと感じた。

無理に権限を超えない

エンジニアの採用が行き詰まっていたので、エンジニア専用の企業noteを作成し文化を外部公開し応募者数を上げようとした事がある。
これは権限を超えた行為であり、かなりの代償を伴うことを知った。
メールの文言を一語一句役員が管理する会社だったので、外部に公開する文書に関しては厳しいレビューが入る。
エンジニアが自分達で運用し公開するくらいの温度感で考えていたので、当初考えていたコストと全く違った。
一つの物事にコストを割り当てると、もう一つの物事からは引かれることになる。
その結果発生するのは、プロダクトの質の低下。
無理に権限を超えて解決しようとすると本来の役目がおろそかになりかねないので注意が必要。

曖昧にする部分と確定させる部分をハンドリングする

本書には危険ワードの紹介として「よさそうですね」という言葉が載っている。
実際に実施するかしないか、ふわふわとした言葉なので確定させた方がいいとのこと。
役員が隣で働いているような職場なので、この状況は頻繁に発生していた。 この言葉を発した人間が高い権限を有している時は特に警戒すべき(その人の発言で180度ガラッと変わる可能性があるので)。
要件に関しては確定ばかりさせるのも良くない。
あえて曖昧にしてチームに持ち込みメンバーの成長を促す必要性もある。 このことから、あえて曖昧にする部分と確定させる部分を自分の中で落とし込んでいく必要性がある。

好奇心は武器

人と円滑にコミュニケーションを取るためのコツはなんなのだろう。
その回答の一つが本書に記載されている「好奇心」である。
相手に興味を持って話しかけるか、そうでないかでは雲泥の差がある。
興味がない相手と会話するときは、双方伝える労力が大きくなるだろう。 いざという時の味方を増やすために、普段から好奇心を持ち相手に接する事が重要。

言い方一つ

「なぜ、こんなことをしたんですか?」と「いい感じですね!チームがどのように、そのアイデアに行き着いたのか教えていただけませんか?」
どちらが答えやすいだろうか。
大半の人は後者と答えるだろう。
どちらの質問も、聞いてることは同じようなことである。
しかし、感触がまるっきり違う。
言い方一つで良い方向にも悪い方向にも行くのがコミュニケーションの難しさ。
これはプロダクトマネージャーとか関係なく使うべきテクニックなので少しずつ慣れていこうと思う。

まとめ

「プロダクトマネージャのしごと」は技術でなくコミュニケーションを使った解決方法を学ぶことができる。 より運営寄りというかプロダクト寄りというかアウトカムを意識した志向も身につけていきたい。 解決方法は増えるに越したことがない。
技術・マネジメント両方で思考できたらと思う。

Rails7からDBレイヤーを剥がす

RailsからDBレイヤーを剥がす手法について記載します

環境

Rails 7.0.3

bin/setupファイルを修正

bin/setupファイルのdb:prepare部分を削除します

  # 以下2行を削除かコメント化
  puts "\n== Preparing database =="
  system! "bin/rails db:prepare"

config/enviroments/development.rbを修正

  # Raise an error on page load if there are pending migrations.
  # 以下1行を削除かコメント化
  config.active_record.migration_error = :page_load

  # Highlight code that triggered database queries in logs.
  # 以下1行を削除かコメント化
  config.active_record.verbose_query_logs = true

config/database.ymlを削除

config/database.ymlファイルを削除します

dbフォルダを削除

dbフォルダを削除します

modelsフォルダを削除

modelsフォルダを削除します

application.rbを修正

require "rails/all"

を以下に修正

require "action_controller/railtie"
require "action_view/railtie"

他に必要なrailsの機能があるなら、以下を参照しながらrequireする

github.com

rails/allを分解してrequireしたら以下のようなエラーが立ち上げ時に出るので 一つずつファイル内検索して対応する

 `method_missing': undefined method `action_mailer'

基本は設定のところばかりが引っかかるのでコメント化対応でOK

gemファイルからDB関連のライブラリを削除

MysqlやらsqliteなどのDB関連のライブラリを削除して 再度bundle installします

rails sしてエラーなく立ち上がったらOK

以上となります

getOrElse関数が便利

最近、fp-tsについて調べる機会がありました。

github.com

TypeScriptで関数型言語っぽい事が出来るライブラリです。 プロジェクトに盛り込むことも考えましたが 現時点ではメリットが少なく導入には至りませんでした。

調べていく中でgetOrElse関数を見つけて、これ使えるんじゃねと思ったので備忘録として記録します。

getOrElse関数とは

fp-tsのgetOrElse関数はSomeの場合は中の値を返却しNoneの場合は設定した値を返却します。

gcanti.github.io

本来は関数型言語の考え方に則ってSomeやNoneで処理するべきですが プロジェクトに盛り込めなかったということでNullをNoneとして扱う[getOrElse関数もどき]を自作して使ってみました。

自作版 getOrElse関数

以下、自作したgetOrElse関数です。

値が存在する時とNULLだった時で返す型を一致させないとダメなので ジェネリクスで返却時の型を統一しています。

const gerOrElse = <T>(func: () => T) =><T2>{
  return (val: T2 | null) =>{
    if(val === null){
      return func()
    }else{
      return val
    }
  }
}

自作版getOrElse関数を使う

自作版getOrElse関数は、以下の条件の時に使えます

  • サーバ側からのレスポンスがnumber | nullやstring | nullの型を持っている
  • nullだった場合デフォルト値を適用して処理を継続しなければならない

自作版getOrElse関数を使ってコードを修正してみました。 まずは改善前のコードを掲載します。

// 改善前のコード

type Info = {
  name: string,
  kana: string,
  testFlg: boolean,
  zip: null | string,
  birthday: null | string,
  other: null | string,
  age: null | number
}

const createEditForm = (info: Info) =>({
  name: info.name,
  kana: info.kana,
  testFlg: info.testFlg,
  zip: info.zip === null ? "" : info.zip,
  birthday: info. birthday === null ? "" :  info.birthday,
  other: info.other === null ? "" : info.other,
  age: info.age === null ? 0 : info.age
})

次に改善後のコード。

// 改善後のコード

const gerOrElseNumber = getOrElse(()=> 0)
const gerOrElseStr = getOrElse(()=> "")

type Info = {
  name: string,
  kana: string,
  age: number
  testFlg: boolean,
  zip: null | string,
  birthday: null | string,
  other: null | string,
}

const createEditForm = (info: Info) =>({
  name: info.name,
  kana: info.kana,
  testFlg: info.testFlg,
  zip: gerOrElseStr(Info.zip),
  birthday: gerOrElseStr(info.birthday),
  age: gerOrElseNumber(info.age),
})

三項演算子が多く見栄えが悪かったですが、getOrElse関数で一掃しました。

今年採用してよかったドキュメント類

今年からプロジェクト内で属人化を防ぐために
ドキュメントの運用文化をプロジェクトに取り入れてきました。

採用してよかったドキュメント類をまとめていきます。

ユーザストーリー

取り入れた理由

何のために存在しているのかが理解できない機能が増えてきているので取り入れました。 機能を担当していた人が退職して情報が何も残っていないパターンです。
その機能を消していいのかも分かりません。

記載する内容

  • 依頼者は誰なのか
  • その機能がどんな目的で誰が使うのか
  • 受入条件は何なのか

テンプレート

依頼者:
依頼者を記載する

概要:
何故この機能が必要になったのか   
この機能を使って達成したい目的は何なのかを記載する

受入条件:
受入条件を記載する

ストーリー:
実装された機能を実際にどうやって使うのかを記載する

サンプル

実際にどのような使われ方をするのかをストーリー形式で記述するのが特徴です。

依頼者:
B部門XXXさん

概要:
[B部門]が
[顧客のパスワード]を[リセットしたい]
なぜなら、[顧客側の環境でパスワードがリセットできない時に社内側で対応したいから]

受入条件:
B部門が顧客のパスワードをリセットできるようになる事

ストーリー:
[B部門]: 顧客側からパスワードをリセットしたいが出来ないと連絡が入った。
[B部門]: 特別にこちらで対応しよう。
[B部門]: システムのAページに移動してパスワードリセットボタンを押下。
[B部門]: 対応完了。

ストーリー形式でまとめることによって、どのようなフローで機能が使われるのかを
知る事が出来ます。

自分担当しない機能が実装された時、背景を知るのに便利でした。

ガッチリしたドキュメントでなく楽に書けるのでそこもポイント高いです。
今までドキュメントを運用する事がなかったチームでも楽に書けると思います。

ADR

取り入れた理由

ソフトウェアアーキテクチャ・ハードパーツを読みADRの利便性に気が付き取り入れました。

www.oreilly.co.jp

新しいアーキテクチャの適応や最適化業務に対して より詳細に分析するフェーズを設けられるようになったのは、この本のおかげです。

記載する内容

  • 何故その設計を採用したのか
  • その設計を受け入れた場合どのような恩恵があるのか
  • その設計を受け入れた場合のデメリット
  • 他に検討した案

テンプレート

コンテキスト:
何故、このADRが生まれたのかを記載。  
問題が何なのか。

決定:
複数ある案の中から何故この決定を選択したのかを記載する

影響: 
この決定を受け入れた場合に発生する影響を記載する  
例えば、テストが増えるとか新しい作業工程が発生するとか

サンプル

サンプルの方はソフトウェアアーキテクチャ・ハードパーツの方に
沢山掲載されているので、そちらを覧ください。
ここで適当なサンプルを見るより絶対にそっちを見た方がいいと思います。

開発環境の立ち上げ手順

取り入れた理由

新規メンバーが増えてきて各マイクロサービスの立ち上げ方法を説明するのが面倒になったので
githubのReadme.mdに載せて各メンバーが参照できる状態にしました

記載する内容

  • git cloneするところからの立ち上げ手順
  • どのような状態になったら開発がスタートできるのかを明確に記載する

テンプレート

なし

サンプル

コマンド列挙するだけでOK
前準備が長い場合はmakeコマンドにまとめちゃいましょう

以下のコマンドを実行して環境を構築する

git clone xxx
cd xxx
make build
make reboot

http://localhost:9999にアクセスして画面が出たら完了