SSブログ

バックエンドサービス開発で当たり前に行ってきたこと [プログラマー現役続行]

2000年以降に継続的インテグレーションが広まっていき、今ではCIということで当たり前に行われています。Go言語でウェブサービスのバックエンドサービスを開発するようになって6年ほど経過していますが、日々の開発の中で私自身が当たり前と思って行ってきたことがいくつかあります。
  • ローカル実行:自分の開発用マシン(MacBook Pro)で、開発しているサービスの自動テストをローカルで実行できる
  • カバレッジ:テストを実行した結果、どの行が実行されなかったかをカバレッジで確認できる
  • 並列実行:Go言語の`testing`パッケージの`t.Parallel()`を使って並列にテストを実行することで、テスト対象のサービスに並列にリクエストを処理させる
  • 長時間ランニングテスト長時間ランニングテストができる
これらすべてができないとバックエンドサービスが開発できないわけではありません。そして、次のように反論されるかもしれません。
  • ローカルで実行できなくても、CIですべてのテストを実行できています
  • どの行が実行されていないかを確認しなくても、CIですべてのテストがPASSしているので問題ないのでは?
  • 逐次的にすべてのテストがCIで実行されてPASSしているので問題ないのでは?
  • ローカルで長時間ランニングテストをしなくても、CIでテストが1回PASSしているので問題ないのでは?
おそらく、この反論に対して相手が納得する説明を行うのは容易ではないです。なぜなら、私自身が当たり前に行ってきたことが何をもたらすかは、実際に行ってみてどのような問題を見つけるかを経験してみないと、容易に納得できないからです。
コメント(0) 

株式会社令和トラベルを退職します [転職]

2023年12月1日から働き始めた株式会社令和トラベルを3月19日付けで退職します。1984年4月1日から社会人として働き始めてから9社目の会社でした。9社の中で最も在籍期間が短かった会社となります。

私自身は、今年の11月で65歳になります。ウェブサービスの業界で働き続けるとしたら、API仕様ファーストおよびE2Eテストによるテストファースト開発を経験するエンジニアを増やしていければと思っています。もちろん、私自身もソフトウェア開発を続けたいのは以前と変わりません。しかし、私自身が正しいと思わない方法でソフトウェア開発を続けたくなかったので退職することにしました。

API仕様ファーストとその仕様をテストする自動テストを(テストファースト開発で)整備しながら開発をするというのは、私自身はウェブサービスのバックエンドサービス開発に従事してから始めたことではありません。API仕様をきちんと記述するというのは1993年頃から行っており、API仕様を自動テストするといのは、2003年以降に富士ゼロックスおよびリコーでの2つのデジタル複合機のコントローラソフトウェア開発で行ってきたものです。

何歳までソフトウェア開発に従事できるかは分かりませんが、同じような経験を積んでくれる開発を一人でも増やせればと思っています。

良かったこと

令和トラベルで働いてよかった点を挙げると、以下のとおりです。
  • TypeScriptおよびGraphQLを学ぶきっかけになりました。どちらも、専門家にはまだまだほど遠いですが、強制的に学ぶきっかけになりました。
  • メルペイやカウシェでGoで構築したE2Eテストフレームワークの基本的な考え方を適用して、TypeScript/JestでE2Eテストフレームワークを作成して、新たな機能をE2Eテストを書いて実装しました。
モックを多用した既存の単体テスト(結合テスト?)から、長期的にE2Eテストを整備することへシフトしないことになったのは残念です。E2Eテストフレームワークに関しては、外部サービスのフェイクサービスを実装するところまではいきませんでした。

Jestテストフレームワークでは、テストファイル単位でプロセスが生成されて実行されるため、外部サービスのフェイクサービスを同じプロセス内で動作させることはうまくいきません。したがって、別プロセスとしてフェイクサービスを起動して、テストコードとそのフェイクサービス間で通信をしながらレスポンスやエラーを設定をする必要があるのは分かっていたのですが、そこまでは着手できませんでした。

※ 私は、モックフレームワークを多用したテストは、昔から好きではないです。『Googleのソフトウェアエンジニアリング ―持続可能なプログラミングを支える技術、文化、プロセス』の「13.5 本物の実装」(p.307)で議論されています。

今後

退職後は、業務委託でどこかの会社を手伝うことはあっても、会社勤めはしない予定です。
コメント(0) 

公開API経由のテスト [API仕様ファースト開発]

「API仕様ファースト開発」では、バックエンドサービスが提供するフロントエンド向けのAPIの仕様を策定して、そのAPI仕様に記述されたエンドポイントを直接呼び出すE2Eテストを作成していきます。つまり、バックエンドサービスの公開API経由のテストとなります。

公開API経由のテストに関しては、『Googleのソフトウェアエンジニアリング』の12.2.2「公開API経由のテスト」に記述されています。そこからいくつか抜粋して紹介します。

Googleのソフトウェアエンジニアリング ―持続可能なプログラミングを支える技術、文化、プロセス

Googleのソフトウェアエンジニアリング ―持続可能なプログラミングを支える技術、文化、プロセス

  • 出版社/メーカー: オライリージャパン
  • 発売日: 2021/11/29
  • メディア: 単行本(ソフトカバー)

テスト対象システムの要件が変化しない限りテストが変化する必要がないことを保証するためのプラクティスをいくつか見ていこう。このことを保証するのに群を抜いて最も重要な方法は、テスト対象システムのユーザーが呼び出すのと同じ方法でシステムを呼び出すテストを書くことである。それはつまり、システムの実装の内部的詳細部分ではなく、システムの公開APIに対して呼び出しを行うということだ。

公開APIのみを利用するテストは定義上、テスト対象システムに、そのシステムのユーザーがアクセスするのと同じやり方でアクセスする。そのようなテストは明示的な契約を結ぶので、より現実的であり、脆さがより低い。つまり、そのようなテストが破綻するなら、システムの既存ユーザーの活動もまた破綻するだうということを必然的に意味する。それらの契約のみをテストするというのは要するに、テストにつまらない変更を加えた結果についていちいち心配する必要なしに、システム内部のリファクタリングはどんなものでもやりたいようにやれるということだ。

Googleでは、公開API経由のテストは実装詳細に対するテストより優れているという点を納得させるために、エンジニアを説得しなければならない場合があることがわかっている。エンジニアの気が進まないのは理解できる。自分が書いたばかりのコードに専念するテストを書く方が、そのコードがシステム全体にどう影響するか理解するよりずっと楽な場合が多いのだ。それにもかかわらず、そのようなプラクティの奨励には価値があることがわかっている。そのプラクティスに従うという追加的な労力を先行して費やすと、保守の負担が減るという形で、かけた労力の何倍もの見返りがある。公開APIに対するテストは、行うことでテストの脆さが完璧に防げるわけではない。しかし、システムに対して意味のある変更が起こった場合のみテストが失敗するよう担保するために行える対策として、最も重要なのは、公開APIに対するテストだ。

コメント(0) 

Hotfixとテスト失敗 [プログラマー現役続行]

何らかの緊急の障害に対して、Hotfixとして緊急に修正がリリースされることはよくあると思います。そのHotfixリリースのPull Requestを見たときに、既存のテストの修正が含まれていない場合、次のことが起きていることが分かります。
  • 修正に該当する部分の機能をテストしているテストコードが存在しなかった。存在していればそのテストが失敗するので、一緒に修正されているはずです。
  • 既存のテストが存在しないため、既存のテストに追加/修正を加えるといった再現テストの作成ができなかったし、新たに作成する時間もなかった。
緊急なので、後者も仕方ないです。しかし、リリース後に不足しているテストコードを作成したいものです。ソフトウェアエンジニアなら、後からでもよいので、テストを追加する習慣を身に付けるようにしてください。
コメント(0) 

Mission Abandoned [API仕様ファースト開発]

2017年8月末に株式会社リコーを退職し、ソラミツ、メルペイ、カウシェとバックエンドサービス開発に従事してきました。最初の頃は、初めてのバックエンドサービス開発なので、どのように行うのがよいのかは手探りに近かったのですが、メルペイで現在も運用されているマイクロサービスの1つを開発してから、さまざまなバックエンドサービス開発を通して、得た知見や行ってきたことを雑誌の書籍やTechブログとしてまとめてきました。その主なものは次の通りです。
  • 「API仕様ファースト開発」
  • 1つのサービスを単独でテストするE2EテストフレームワークとE2Eテストの整備
カウシェを退職した理由は、カウシェではこの2つが、かなりきちん開発プロセスとして定着させることができたので、別の会社で同様な経験を持つエンジニアを増やしたいというものです。特に「伸ばすのが難しい能力」で述べた最初の2つ(「API仕様をきちんと書ける」「テストファースト開発ができる」)を経験したエンジニアを増やしたいというものです。

WEB+DB PRESS Vol.134

WEB+DB PRESS Vol.134

  • 出版社/メーカー: 技術評論社
  • 発売日: 2023/04/22
  • メディア: Kindle版

記事「実践API設計」では、E2Eテストを書いて実際にどのコードが実行されたのかをカバレッジで確認することも重要だと述べています。

しかし、これらを全く行っていなくても、サービスの開発はできます。だから、経験していない開発者が多数いるわけです。今回は、きちんと経験した開発者を増やして、私が健全だと思う開発プロセス構築を目指すというMissionはAbandonedとなります。
コメント(0) 

腰部脊柱管狭窄症の再発(その後) [脊柱管狭窄症]

昨年11月に再発した腰部脊柱管狭窄症ですが、一時期かなり悪い状態になりそうでしたが、その後回復してきました。

天気がよい日は自宅でのエアロバイクではなく、ウォーキングを最近はするようになったのですが、少し前までは、2kmを歩くのに途中3回休憩しないといけない状態でした。歩いているうちに足が痺れてきて辛くなるので、休んでまた歩きます。いわゆる「間欠跛行(かんけつはこう)」状態です。

それでも今週はかなりよくなって、足の痺れは少しありますが、途中休憩なしで歩けるようになりました。このまま良くなれば、もう少し歩く距離を伸ばしていきたいと思っています。
コメント(0) 

健全なバックエンドサービス開発 [API仕様ファースト開発]

ウェブサービスのバンクエンドサービス開発に従事してすでに6年以上経過しています。そして、最初から「API仕様ファースト開発」を行っています。「API仕様ファースト開発」は、私自身の造語です。

バックエンドサービスを開発する際に、(無意識に)私にとって当然だと思っていたのは次の2つです。
  • フロントエンドを提供するバックエンドサービスのAPIでは、その仕様をきちんと記述する
  • API仕様通りに個々のエンドポイントが動作するかを検証する自動テスト(E2EテストフレームワークとE2Eテスト)を整備する
この2つが最初にありきなので、実際のバックエンドサービスの構造をどうするのか、内部の各モジュールのテストをどうするのかといったことは、最優先とはなりません。

一般的には、単体テスト・結合テストを開発しながらソフトウェアは行うと考えられていますが、上記2点を優先して開発した場合、E2Eテストが実行されて、カバレッジが確認できれば、そこからあえて、単体テストや結合テストを書くことはしません。

なぜなら、常に、API仕様に基づいてテストファーストでE2Eテストを開発し、その後実装することを行っているからです。E2Eテストですでに機能の確認が行われていれば、さらに単体テストや結合テストを作成することは無駄であり、その工数を費やすことに意味がないからです。
私自身は、API仕様を記述せずに、1つのバックエンドサービスとしてそのエンドポイントを直接呼び出す自動テストコードも存在しないことが正しい開発だと思わないだけです。私は、そのような開発をしていたら、不快に思うだけです。

不健全なバックエンドサービス開発

私にとって不健全に思われるバックエンドサービス開発は、次の通りです。
  • API仕様が記述されず、バックエンドサービスのエンドポイントのテストはフロントエンドを接続して手作業で行う開発。
  • 仮にAPI仕様が記述されていたとしても、バックエンドサービスのエンドポイントのテストはフロントエンドを接続して手作業で行う開発。
  • 単体テストや結合テストでテストしてるから「大丈夫なはず」という開発。
    • API仕様が記述されているなら、バックエンドサービスのエンドポイントを直接呼び出すE2Eテストで検証する方が素直
    • API仕様が記述されていない場合、単体テストや結合テストの妥当性をきちんと確認する方法がない

私自身は、過去2回行ったテスト駆動開発によるデジタル複合機のコントローラソフトウェア開発(通算、約6年)でもテストコードはすべてAPI仕様に記述された呼び出しポイントを呼び出すテストを書いたり、書くことを指導してきました。その経験から、ウェブサービスのバンクエンドサービス開発に従事するようになっても、最初に述べた2点を優先して、当然だと思ってきました。
コメント(0) 

API仕様の技術的負債の返済で遭遇する問題 [API仕様ファースト開発]

API仕様の技術的負債が積み上がっている「言い訳」の一つに次のようなものがあります。
バックエンドサービスとそれに対応するフロントエンドサービスを同じソフトウェアエンジニアが開発しているので、API仕様を書く必要がない。

本当にそうでしょうか。また、テストに関しても、バックエンドサービスのAPIのエンドポイントを直接呼び出してテストするE2Eテストはなく、単体テストやモックを駆使した結合テストしかなくて、最終的な動作はフロントエンドを使って手作業で確認していることがあります。さらに、これらのテストも開発担当者任せになっていることがあるかもしれません。

開発者担当者任せになっていても、「テストが書かれていて、そのテストがCIで実行されてPASSしたものだけがマージされる」という今日では最低となる基準は満たすことになります。そして、さまざまなサービスを「爆速」で開発していると外部に対して宣伝しているかもしれません、

技術的負債の返済で遭遇する問題

API仕様の技術的負債の返済には、次の二つを最初に行う必要があります。
  • API仕様の書き方を決める
  • E2Eテストフレームワークを構築する
これらの詳細は、「WEB+DB PRESS Vol.134」の特集1を参照してください。

E2Eテストフレームワークを構築したら、次は、既存のエンドポイントの修正を通して、技術的負債を返済するか、新たなエンドポイントをきちんと開発するかのどちらかです。どちらの場合も、開発組織へ新たに参加した開発者が実際に行ってみると遭遇する問題があります。それは、次のことです。
既存のコードにバグがあり、期待どおりに動作しない。そのため、その原因の調査に時間を要する。

既存のエンドポイントの修正であれば、すでにコードが存在するので、「既存のコードにバグがあり」ということはあります。では、新たなエンドポイントの作成ではどうでしょうか。

データの準備で躓く

新たなエンドポイントを作成する場合、そのエンドポイントを呼び出すための前提条件として、必要なデータをあらかじめDBに作成する必要があります。サービスを新規に立ち上げるのでなければ、そのようなデータを作成するための既存のエンドポイントは存在することが多いです。だだし、API仕様が書かれていなくて、E2Eテストも存在しないでしょう。

新たなエンドポイント用のE2Eテストにおいて、事前準備として既存のエンドポイントを呼び出してデータを作成する際に、期待通りに作成されないというバグに遭遇することがあります(作成されているようだが、エンドポイントの呼び出しのレスポンスで作成されたデータが正しく返ってこないとか)。

私自身が経験したこととしては、作成されたデータがレスポンスで期待通りに返ってこないというものです。GraphQLでは、要求しないと値が返ってこないので、すべてのフィールドを要求するようなクエリを書いて呼び出すと一部のフィールドが返ってこないというものです。

このような場合、新たなエンドポイントの実装に入る前に、既存のエンドポイントのバグを調査することになります。そして、見知らぬコードの世界で、バグの原因を見つけるのに数時間を費やすことになるのです。

この場合、新たに作成しているE2Eテスト内でそのバグの再現テストは作成していることになります。つまり、期待通りのレスポンスが返ってこないので、本当に作成されたのか分からず、テストが失敗するようになっていればよいです。

バグの原因を見つけたら、それを修正して、E2Eテスト内のデータを準備する部分がPASSするのを確認すればよいわけです。ところが、この実装の修正によって、次のどちらかが発生します。
  • 既存の単体テスト(や結合テスト)が失敗する
  • あるいは、既存の単体テスト(や結合テスト)が何も失敗しない
前者の場合、失敗しているテストコードをよく見ると、テストコード自身が間違っていることが多いです。後者の場合、そもそも必要なテストが書かれていません。

(1990年代まで当たり前であった)実装からテストまですべて開発担当者任せになっているサービスでは、このような問題に遭遇して、既存のバグ調査だけで1日が終わってしまってもおかしくはないです。
コメント(0) 

Pull Requestと「謙虚、尊敬、信頼」 [API仕様ファースト開発]

Googleのソフトウェアエンジニアリング ―持続可能なプログラミングを支える技術、文化、プロセス

Googleのソフトウェアエンジニアリング ―持続可能なプログラミングを支える技術、文化、プロセス

  • 出版社/メーカー: オライリージャパン
  • 発売日: 2021/11/29
  • メディア: 単行本(ソフトカバー)

一年半前に読んだ本ですが、あらために2章「チームでうまく仕事をするには」を読み返してみると最初に、次のように述べられています。
本章において決定的に重要な考え方は、ソフトウェア開発とはチームによる取り組みであるということだ。そして、エンジニアリングのチームで(あるいは他のどんな創造的共同作業でも)成功するためには、「謙虚、尊敬、信頼」という中心的原則をめぐる自身の行動を改革する必要がある。

また、9章「コードレビュー」(p.197)からの抜粋ですが、コードの変更については次のように述べられています。
変更にLGTMの印が付いた後で、作者はその変更をコードベースへコミットすることを許されるが、それは作者が全コメントを解決し、その変更が承認されることを条件としている.
GoogleでのコードレビューのプロセスはGitHubを使ったPull Requestとは異なりますが、基本的には承認されることが条件となります。

私自身がGitHubを仕事で使うようになったのは、2017年9月からソラミツで働き始めてからでした。それから、メルペイ、カウシェと働いてきましたが、PRをdevelopブランチやstagingブランチへマージするには承認(approve)が必須でした。

チームの他のメンバーにレビューを依頼し、コメントを通した指摘の内容を理解して、適切な対処(コードの修正やコメントを通した説明、etc)をして、承認されてから、そのPRをマージするということです。そして、重要なのは、個々のソフトウェアエンジニアの役割(アーキテクト、テックリード、チームリーダー、etc)に関係なく、同等に承認を必要とする運用です。

もちろん、レビューしてもらって承認してもらったからと言って、完璧なコードであることが保証されたわけではありません。そうではなく、「チームによる取り組み」として、このようなプロセスを行い、集団的英知を増やし「バス係数」(プロジェクトを完全に破綻させるために必要な、バスに轢かれる人の数)を高めることになります。

しかし、このような運用ではなく、ソフトウェアエンジニアの役割によっては、レビューしてもらう必要も承認も必要とせずにPRをマージできるが、他のソフトウェアエンジニアはレビューと承認を必要とする運用ではどうでしょうか。PRの承認を要求される役割のソフトウェアエンジニアにとって、承認なしでマージされるPRすべてがベストプラクティスのようなお手本であれば、おそらく何も問題はないかもしれません。しかし、何を行っているのか分かりにくコードだったり、十分なテストコードが書かれていなかったり、必要な説明が書かれていなかったりするかもしれません。

あるいは、きちんとレビューして、コメントをしたにもかかわらず、コメントに対する修正内容を再度確認することを依頼されることなく、PRの作成者の判断でマージされたらどうでしょうか。そうなると、次からはきちんとレビューする気が失せるかもしれません。このようなことが起きないように、レビューと承認を必要とするプロセスをチーム全体で運用するわけです。

一部のソフトウェアエンジニアだけが、Pull Requestを承認なしでマージできる運用は、長期的な視点では「謙虚、尊敬、信頼」を破壊するベストプラクティスかもしれません。
コメント(0) 

API仕様ファースト開発(2) [API仕様ファースト開発]

よく知られている「Be the Worst」(最低である)は、以下の書籍で述べられています。

情熱プログラマー ソフトウェア開発者の幸せな生き方

情熱プログラマー ソフトウェア開発者の幸せな生き方

  • 出版社/メーカー: オーム社
  • 発売日: 2017/07/15
  • メディア: Kindle版

アプレンティスシップ・パターン ―徒弟制度に学ぶ熟練技術者の技と心得 (THEORY/IN/PRACTICE)

アプレンティスシップ・パターン ―徒弟制度に学ぶ熟練技術者の技と心得 (THEORY/IN/PRACTICE)

  • 出版社/メーカー: オライリージャパン
  • 発売日: 2010/07/08
  • メディア: 単行本(ソフトカバー)

『情熱プログラマー』の「一番の下手くそでいよう」では、次のように述べられています。
君の周りにいる人たちが君自身のパフォーマンスに影響する。
仲間は慎重に選べ
『アプレンティスシップ・パターン』の「最低である(Be the Worst)」では次のように述べられています。
あなたより優れた開発者に囲まれるようにしてください。あなたが最低のメンバーであり、成長する余地がある、より強力なチームを見つけてください。

優れた開発者の定義には、おそらくいくつかの側面があります。たとえば、次の二つもそれらに含まれると思います。
  • 使っている技術に関して深く熟知している
  • ソフトウェアエンジニアリングとして優れた方法を実践している
開発しているシステムの実装の詳細に熟知していて、ドメン領域に関する知識も多いというのは、残念ながら優れた開発者の定義には入らないと思います。従事している期間が長ければ長いほど、熟知するのは当たり前だからです。

ソフトウェアエンジニアリングとして優れた方法を実践しているのかにも、さまざな側面があります。たとえば、継続的インテグレーションを行うことは、今日では当たり前ですが、残念ながら今でもできていない多くの開発組織が存在するのではないかと思います。

あるいは、「API仕様ファースト開発」で述べたような開発プロセスを実践できている組織と全くできていない組織では、どちらの組織に参加するかによって、成長は大きく異なってきます。

ソフトウェアエンジニアリングとして優れた方法を試したり、導入したりするのは、実はあまり簡単ではありません。私自身の過去の経験からしても、従来の方法を変えることに対する現場のエンジニアの抵抗(というより慣性)により、開発現場からの自発的な改善には結びつかないことが多いです。

リコーを退職する以前の富士ゼロックスやリコーでの開発では、強い権限を持つ立場(たとえば、開発の部門長という立場)を利用して、さまざなことを強制して改善させることが多かったです。リコーを退職後は、いわゆるスタートアップ企業で1人のソフトウェアエンジニアとして働いているので、「API仕様ファースト開発」などは「まずはやってみせる」というところか始まります。そして、実際に改善するかしないかは、組織のメンバー次第です。
コメント(0)