サーバーサイドから考える、理想の開発スタイル

ぼくのかんがえたさいきょうの…という記事。 特に目新しい話はなく、よく聞く考え方の整理となる。

なお、特に前半はエンジニアに限らない話も混じっている。

考える目的

エンジニアとして、楽しく充実した開発と運用をする為である。 イケていてシンプルで効率的な体制を目指す。 これにより、エンジニアの幸せのみならず、使ってくれる方へ届くものの価値向上や、プロジェクト的な成功へも貢献できるはずだ。

前提

  • 「Web (API) + クライアント」の構成での話
  • ここでは、サーバー側のコードを書く人数は 2, 3 〜 7, 8 人程度で考えている
  • 様々な背景を考慮しない状況での話であり、状況に応じてここから変化させる
    • チームメンバーの好みにも寄り添う
  • 現時点版
    • 技術や制約、ニーズの変化により、長くないスパンで変遷すると予想させる
  • ツッコミ歓迎
    • 異論は認める、製錬させたい

全体方針

長々と書いていくが、全てこの原則に則る。

  • シンプルで自然を重視
  • 共通化
  • 楽をする為の努力を惜しまない
    • 下地に時間をかけて良い
  • 設計や、運用イメージを大切に

メンバーの姿

  • 求められる2つの力の軸
    • 技術力
      • 実現力
      • 後の運用まで見越した設計
    • サービスへの理解力
  • 挑戦する
    • もはや福利厚生
  • 常に改善を行う
    • 時間を割ける体制を用意しておく

情報共有

  • 隠さなければいけないもの以外は基本オープンにする
  • 情報への入り口を、分かりやすくする
  • チャットルームは増やし過ぎない
    • できるだけ統一したものをつかって、不便であれば慎重に分割する
  • 会議を不要に行わない、テキストで十分なものはテキストで
  • 会議や口頭での会話は、できるだけテキストに落として共有する
  • この辺りの細かいことは、以前ブログに書いているので参照されたい

別職種との連携

  • コミュニケーションしていく
  • 特に自動化はエンジニアの得意領域のため、こちらから改善の提案する
    • 企画者さん、デザイナーさんが、無駄に消耗しているかも

ツール

  • 外部に有用なツールが存在する場合は、積極的に利用する
  • 料金が発生しても、独自につくってメンテナンスコストが発生するよりお得

チャット

  • サービス連携がしやすいチャットツールを使う
    • やりにくいチャットツールをメインで使わなければならない状況下では、開発用として別途 slack を立てるのは許容したい
    • 乱立は避けたいのだが…
  • 連携ツール
    • デプロイ
    • エラーやアラート、パフォーマンス監視
    • その他ツールのアクティビティ(GitHub, wikiツールなど)
  • メールで通知するものは、チャットでも同時に通知したい
    • その場で会話が開始できる

資料

  • 属人化を避ける

ツールの使い分け

  • GitHub Issues
    • 手軽なエンジニアレベルの一時的な問題や作業上げ用として使いやすい
  • README.md, doc/*.md (git repository 内)
    • PR 運用でき、コードと合わせてコミットできる為、複数人での作業に向いている(比較的、コードに追従しやすい)
    • 使い捨てでない手順、エンジニア用のドキュメントに
  • wikiツール
    • 高機能で、非エンジニアも使いやすい
    • 他職種との共有が必要な、仕様書などに
  • Office, Google Docs
    • 一時的な共有に使うのには便利だが、後からは見にくい為、wiki に寄せられるものは寄せるとやりやすい

技術(手順系)

  • そもそも「手順」というものが、できるだけ存在しなくていいように、またはシンプルになるように
    • 例えば、1コマンド打つだけで終わったり、自動化されていたり
  • 初期構築は、README を見て、必要環境の確認をして1コマンドで完了、くらいに
  • コピペする手順書を避ける
    • イレギュラーな作業の為の手順書を作るのは問題ないが、日常的に同じものをコピペするような運用の手順書は不要
    • 共通で使えるようにする

技術(設計・仕様書

  • できるだけ自動生成させる

手で書く部分

  • リポジトリにマークダウンとして配置
  • コードを触ると同時に直すようにする

API

  • どういう資料が必要か

    クライアント側: サーバーの API 一覧と仕様

    サーバー側: クライアントのどのアクションでどの API が呼ばれるか

  • 前者は一般的であり、確実に欲しい

  • 後者は、複雑なシーケンスを踏む API がある場合に、あると便利
    • クライアントのどのアクションで、どんなリクエストがサーバーに送られてくるか分かるように

API

  • RESTful をできるだけ崩さない
  • JSONYAML で仕様を書く
    • いくつかのツールがあるので、良さそうなものを
      • JSON Hyper-Schema
      • RAML
      • Swagger (OpenAPI)
      • など…
    • 個人的には、Swagger を使いたい
  • API 仕様からつくるか、実装(コード)からつくるか、という選択肢がある
    • 後者のイメージとしては、テストやモデルの実装から仕様を吐く、など
    • API はコードとは分離して考えたい為、ここでは前者で
    • コード自体を知らない人も、仕様が書ける
  • これによりできること
    • ドキュメント生成
    • モックサーバー立て
    • サーバー連携
      • リクエストやレスポンスのパラメーターの検証
      • テスト
      • モデルに紐付けて連携
    • API クライアントや、テスト用の REPL の生成

図で表した。

f:id:misoobu:20160228213837p:plain:w400

  • レスポンスの形式
    • 一貫性のあるものにする
      • 全体の構造や、時間の表記はどうするか?エラーは?など
    • JSON API などの規格を採用したり(名前が分かりにくい)、ガイドラインを定めたり

コード設計、コード

  • 綺麗でシンプルを目指す
  • API は REST から崩さず、DB もシンプル(非正規化などを行わない)な所から考え始める
    • 後から必要に応じて、まとめたり崩したりする
    • 例えば API において、開発初期段階では1機能で複数API を叩く形を許容する
  • 運用まで想定してつくる
    • 運用のために内部設計を崩すことはしない(両立させる)
  • コード規約を導入する
    • メンテナンス性を考え、アプリ固有の設定はしすぎない
    • 規約違反は自動で通知、修正する
  • 頻繁に更新される(運用で追加される)とデータと、ソースコードは、リポジトリを分けるなどして分離する
    • 役割の明確化、コミットログの追いやすさ、コードデプロイとデータ適用の切り分けの為

コードのテスト

  • 適切な粒度を決めて書く
  • 前述の API spec は、ごく簡単なテストにも利用できる
  • 自動でテストを回し、通知する

ブランチ運用

  • 基本的に、master からブランチを切って、master へ PR する
  • master は常にデプロイ可能な状態を保つ
  • 本番環境は、常に最新の master ブランチでデプロイされた状態を保つ
    • master へマージしたらすぐデプロイ。後述のように自動化
  • ブランチ名は分かりやすく適当なものを付ける
    • feature などの prefix は必要にならないので付けない
  • ただ develop ブランチを追加したからといって、確認が厚くなるわけではない
    • フローを増やしたくない
    • 適切に(無駄なく)確認できているか

Pull Request

  • 最新の master で rebase した状態で作成し、マージ前も最新に追従させる
  • タイトルや説明文、コミットメッセージは、分かりやすくつける
  • レビューと修正を行う(当然)
  • CI が実行され、ステータスを表示する
  • 自動的に確認用環境へデプロイされる
  • できるだけ出しっぱなしにしない
    • つくったら時間を空けずに本番まで持っていく
    • 管理しない為
    • この為に、機能の開放時期を制御可能にしたり、API のバージョニング(後述)を行う

デプロイ

  • 本番環境は、master を更新すると、自動でデプロイされる
    • 別作業が必要な場合は手動で行えるように
  • デプロイ中は、随時チャットへアクテビティが投稿される
  • マージとデプロイはセットの為、デプロイ時のタグ打ちや開発者用リリースノートは不要
  • デプロイ自体に時間がかからないようにする

自動化、CI

  • 人間がやる必要のない定常的な作業は、自動化する
  • 自動実行するものの例
    • テスト
    • コード規約
    • デプロイ
    • ライブラリアップデート(PR 作成まで)
  • 便利なサービスがあれば活用したい

クライアントとサーバーのバージョニング

ここでは、クライアントのアップデートタイミングを細かく制御出来ない、モバイルアプリの場合について。

  • 破壊的な API 変更がある場合、サーバーは API バージョンを進め、新クライアントもそちらへ向き先を変える
    • API の変更の為にメンテナンスに入れる必要がない
  • サーバーは、直前のバージョンの動作を一定期間保証する
    • バージョン更新後、一定期間待った後にバージョンアップ必須とする
    • 利用者は、クライアントアプリのバージョンアップタイミングを多少コントロールできる

メンテナンス

  • 基本的にやらない
    • コストをかけてでもやらない(メンテナンスに実は大きなコストが掛かっていることも)
    • 無停止で DB を migrate する手法はいろいろある
  • 利用者にとっても、ないほうが良い
  • メンテナンスがあると作業やアップデートを集中させてしまいがち
  • 非常時には発動できるように準備はしておく
  • 関連して、DB の不要な履歴データは自動で少しずつ消えるようにしておく

国際化、他プラットフォーム対応

  • 予定がない時点で対応する必要はないが、対応イメージはできるようにしておくべき
    • 複雑化させない
  • コードベースは1つに保ち、開発コストを抑える
  • 前述の、コードとデータの分離とも相性が良い

管理画面、動作確認用機能

  • なにをしたいのか
    • データの確認
    • 簡単なデータ操作
      • マスターデータ作成や更新(seed 運用しないもの、許可するものとしないものは明確に分ける)
      • 動作確認の為の、ユーザーデータ操作
    • エンジニア以外による、利用者のサポート
  • 要素
    • データビューアーとその簡単な操作
    • アカウントや権限の管理
    • 動作確認用の機能
    • 操作履歴
  • やること
    • API と管理画面は分離する
      • 明確に住み分けることで分かりやすく
      • rails なら同プロジェクト内でエンジン化など
    • 手作りの管理画面 + 管理画面ライブラリによる簡易ビューアー、という構成
      • 徐々に手作りの方へ寄っていくと思われるが、シンプルなデータはライブラリによるもので十分
    • 手作りの管理画面は、使い勝手やデザインを揃えるため、スタイルガイドなどで縛る
    • 管理画面ライブラリは、カスタマイズを行わず、またそこからデータの編集もさせない
      • 要望に合わせて次々にカスタマイズをしていくと、手をつけにくい状態になることも
  • エンジニアによる調査対応が必要になった時、管理画面からも実行可能なようにして、誰でも出来るように

その他

  • 属人化を避ける為、特定の人物に特定の作業を集中させない
  • 名前空間をしっかり切る
    • 運用が進むと、把握の容易さが変わり、効いてくる

これらをやると、メンバーはどうなるか

  • 繰り返しの作業がない
  • 常に新しい、モダンで精錬された環境で仕事が出来る
    • モチベアップ(と表現すると、モチベに左右されるなと偉い人から怒られる?)
    • 環境の更新を切り口に、そのものへの理解を深められる
  • 知らないことが少なく、知らないことを知る方法を知っている
  • => 楽しい!(多分)

どうするとこれらが実現できるのか

理想を心に描いた上で…

  • 既存プロジェクト: できるところから、効果が高いものを少しずつ
  • 新規プロジェクト: 開発が活発で変化が多い時期だが、先を考えて基盤を思考する

常に改善する。コストを払う価値はある。

後書き

全体的に「なぜ?」が足りないように見えるが、よく聞く話の集合体であるので、省略した。

前提の通り、あくまで理想なので、現実でこの通りにやれることはないだろう。 プロジェクト成功の確度が低く、基盤構築に時間を割けないということもあるかもしれない。

しかし、理想を思考することは大切だ。これにより、実際の制約下でも、軸がブレない設計が出来る。 また、これらは、メンバーと話し合って磨き上げ、浸透させることが大切である。

より楽しい開発を目指していきたい。