Skip to main content

 

 

 

 

 
Language:
 
 
 
OutSystems

大規模システムの開発とデリバリー

開発者やチーム、アプリケーションの規模が拡大するにつれて、開発とデリバリーの俊敏性を保ち、モダンアプリケーションプラットフォームにふさわしい開発スピードを維持していくことが重要になってきます。

OutSystemsはミッションクリティカルなシナリオでも高い生産性を実現することを目指し、各組織がビジネスニーズに常に対応可能な優れたソフトウェア開発チームになることをビジョンとして掲げています。この目標を達成するためには、効率的な継続的統合/継続的デリバリー(CI/CD)のアプローチを推進することでリードタイムを減らし、オンデマンドでデプロイを行えるようにすることが最適であるというのがOutSystemsの考えです。

大規模システムの開発とデリバリーは、どのようなテクノロジースタックであっても多くの課題をもたらすものです。同時リリースへの対応や進捗の異なる複数の環境での保守作業にはそれぞれ独自の課題が発生します。たとえば、チームやアプリケーションの依存関係が多すぎる、作業スピードの異なるチーム間の連携が不足している、開発とデリバリープロセスにおける検証時のフィードバックループが迅速でない、といった問題があります。また、複数のアプリケーションを同時に本番に移行する際には、統合の問題や緊急修正が大量に発生することも考えられます。従来は物理的なブランチを作成することでこうした課題に対応していましたが、これには欠点があります。しかもその欠点は、ブランチを長期間使い続けるうちに増大してしまうのです。こうした課題への適切な対処方法として、CI/CDアプローチとDevOpsプラクティスの適用が適切であることが実証データ(State of DevOps 2019レポート)からも明らかになっています。

高品質かつ迅速なデリバリーを行うには、機能トグルなどの定評あるソフトウェアエンジニアリング手法を使用して短期的な分離やロジックの分離を行うほか、開発者のエラーを軽減するために適切なガードレールやチェックポイント(コードレビューや回帰テストの自動化など)を適用することが不可欠です。ロジックの分離には意識改革が必要になることもありますが、最終的にはそれに見合った成果が得られるでしょう。

以下のセクションでは、OutSystemsの調査に基づき、開発チームが拡大する際に発生しやすい課題とOutSystemsによる様々な対処方法について説明します。

チーム構造とアーキテクチャ

チームの生産性は、チームがビジネス目標との整合性と独立性をどの程度確保できるかに大きく左右されます。チームを独立・分離させると、開発スピードが上がります。開発の独立性、デリバリーの独立性、ビジネス目標との整合性、疎結合のアーキテクチャの維持といった面でのメリットも得られます。効果的なチームの分離を実現するには、以下に挙げる側面でファクトリーのアーキテクチャと組織を整備する必要があります。

ドメイン駆動設計

ソフトウェアファクトリーが拡大するにつれ、開発とデプロイの両方が複雑化して時間がかかるようになり、軽微な変更要求の実装さえもままならなくなります。各チームはリスクを負うことを恐れ、計画が立てづらくなるため、開発とデプロイを分離することがさらに難しくなっていきます。このような状況では、あらゆる箇所で相互依存が発生し、関与する意思決定者が増えて他のビジネス部門への影響が大きくなり、各ビジネス部門の要望に応えることが難しくなります。だからこそ、大きなモノリスを扱いやすい小さな要素に分割し、チームの分離とライフサイクルの独立を実現させることが不可欠なのです。

ドメイン駆動設計(DDD)は疎結合のアーキテクチャを構築する際に推奨されるアプローチです。疎結合のアーキテクチャでは、依存関係を最小限にして様々なソフトウェア要素を分離します(自己完結型ドメイン)。疎結合のアーキテクチャでアプリを作成・デプロイすると、拡張性レジリエンス保守性が向上し、開発チーム間の依存関係が少なくなるというメリットがあります。アーキテクチャに対するDDDアプローチでは、ビジネスドメインに対応したコンポーネントとしてソフトウェアシステムを構成します。また、所有権モデルとビジネスドメインが対応し、各ドメインを1つのチームが所有することになります(1つのチームが同じドメインに関連する複数のシステムやコンポーネントを所有する場合もあります)。これにより、各ビジネスドメインをそれぞれの変化のスピードに応じて進化させることができます。

ドメインの定義

OutSystems Architecture Dashboardでは、アプリケーションのアーキテクチャ全体を俯瞰的に見ることができるため、アプリケーション間の想定外の依存関係や不適切なアーキテクチャパターンを検出し、ドメインを適切に評価することができます。

チーム構造

チーム間のやりとりや関係性、依存関係は、チームのアーキテクチャや独立性、ひいては開発スピードにも影響します。コンウェイの法則によると、組織で設計するシステムにはその組織のコミュニケーション構造が反映される傾向があるそうです。つまり、部門が技術別に分けられている組織では、技術ベースのアーキテクチャになるということです。技術ベースの組織を構成するチームは、それぞれの技術特性によって技術指向のアーキテクチャのアプリケーションを作成する傾向にあります。そのため、アーキテクチャの各部分がサイロ化し、ビジネス部門からの変更要求を実装するたびにアーキテクチャ領域に手を加えなければならなくなります。その結果、技術チーム間で膨大なコミュニケーションや調整が必要になるのです。技術ベースのチームは、(同じ機能ドメイン内の)UI担当、API担当、データモデル担当のように分けられます。

逆コンウェイの法則を適用して適切なビジネス指向のアーキテクチャを作成にするには、技術的なトピックではなくビジネスドメインごとに分けたチームで組織を構成する必要があります。このような組織ではサイロは発生せず、必要なすべてのスキルがそろっているため自律性が向上し、コミュニケーションの負荷もなくなります。ビジネスを中心とした組織ではビジネス指向のアーキテクチャが設計されるため、ビジネス目標との整合性が高まり、ビジネスの成長・拡大に対して適切に対応することができます。このアプローチを効果的に実践するには、部門横断型チームが必要です。

部門横断型チームを構成することで、コミュニケーションや品質、スピード、イノベーションを促進でき、自律性と独立性が向上します。部門横断型チームには、アプリケーションやコンポーネントのライフサイクル全体を管理するために必要な職種がそろっています。 そのため、完全な独立性と自律性を持って開発から本番に至るまでのコード変更を推進し、収集したフィードバックにすばやく対応できます。これは単一機能チームとはまったく対照的です。単一機能チームでは、各ライフサイクル機能がサイロ化し、変更を1つ加えるだけでも複数回の引き継ぎが必要になります。しかもこうしたタスクは正式なプロセスを経なければならないため、キューが発生してリードタイムにも影響します。

機能チームと部門横断型チーム

所有権とガバナンス

適切な所有権モデルとガバナンスモデルを持たない場合、複数のチームが同じモジュールを変更してチームやライフサイクルの衝突が発生したり、責任の所在が明らかでないために実行が非効率になったり、組織内でガバナンスが衝突するといったリスクが生じやすくなります。所有権を明確にすると、知識や意思決定が集約され、開発とデプロイのライフサイクルが効率的になります。また、チームの衝突や作業の重複が減り、チームの責任感や士気も高まります。

チームの所有権モデルは、ビジネス組織、ファクトリーの成熟度と規模、利用可能な機能に応じて調整が可能です。各ソフトウェアドメインのオーナーを1つのチームに限定するなど、共有コンポーネントに対して明確で強力な所有権を定義すると、オーナーがいないソフトウェアドメインがなくなり、すべての変更をオーナーが実行するようになるため、チームの衝突が少なくなります。明確に定義された所有権モデルは拡張性が高く、独立した開発とリリースライフサイクルの効率化に役立ちます。所有権モデルはチーム構造と整合性を持たせるようにし、ガバナンスモデルにはこれら両方を反映させる必要があります。

所有権ガバナンスモデル

コラボレーション

アプリケーションポートフォリオ、チーム数、開発者数が増大するにつれて、作業の分離(互いに干渉せずに開発やテストを実施)とコラボレーションの適切なバランスを見つけることが、複数のリリースを管理しながら開発スピードを保つうえで重要になります。この課題への一般的な対策のひとつがブランチです。ブランチを使用すると、進行中の作業を完全に分離してメジャーリリースの保守を継続することができます。ただし、異なるリリースのテストに向けにランタイム分離用の追加環境を管理するといった別のタスクが発生することがあります。また、ブランチを使用することでコード統合の頻度が低くなり、大量のマージが必要になることもあります。北米最大の間接資材サプライヤーであるGrainger社でCTOを務めるJonny LeRoy氏は、「以前はブランチを平行な線として描いていましたが、実際には離れていく線なのです。このように描き直すと、時間の経過とともにマージが難しくなることがわかると思います」と話しています。

ブランチ - 平行な線と離れていく線

OutSystemsは、複数の機能ブランチを管理する手間をなくすことで、マージに伴う問題を回避できる機能を提供し、最適な継続的統合アプローチを実現することを目指しています。そこで一時ブランチ機能を提供して、開発者があらかじめ作業やテストを分離して行い、完成した少量のコードだけを共有できるようにしています。機能を分離することで、開発者は進行中の作業を管理できるようになります。また、トランクベースの開発手法(機能トグルなど)を使用すると、未完成の機能を安全に次の段階に進めることができます。この手法により、マージする未完成の機能を管理し、開発者間の連携やビジネス部門からのフィードバックを増やして、未完成の新しいソリューションを検証することが可能になります。

スモールバッチアプローチ

スモールバッチアプローチを採用すると、複雑なソリューションを小さなコンポーネントに分解し、段階的にデプロイしたり、個別にテストしたりできるようになります。テストに数か月かかるコードを何か月もかけて作成する代わりに、ユーザーストーリーを1つに絞るにするなどコードを少量(スモールバッチ)にすることで、テストやリリースを迅速に行えます。スモールバッチで開発することで開発リスクが低下します。また、ユーザーからフィードバックをすばやく集めて調整できるため、無駄な作業を減らすことができます。

スモールバッチで作業を行うと、ソフトウェア開発が促進され、組織のパフォーマンスが向上します。この特性は、パフォーマンスの向上に寄与するプラクティスや機能に関する独立した学術調査であるDORAのState of DevOps研究プログラムによって明らかになったものです。以下の図は、State of DevOps Report 2019を基に、パフォーマンスに優れた組織とそうでない組織を比較したものです。

パフォーマンスに優れた組織とそうでない組織の比較

スモールバッチでの作業はエラーの予測に役立ちます。また、1回のデプロイで適用される変更の量が少なくなることで問題が起こりにくくなるため、デプロイのリスクも下がります。少量であれば、コードの一部をコードベースにマージしてすぐにテストを行うことも可能です。そうすることで問題をすばやく特定して解決することができます。コードをデプロイ用のコードベースにこまめに統合することでリリース頻度が高まり、ビジネス価値を(リリース頻度が低い場合と比べて)迅速に実現できます。また、エラーが減るため生産性も向上します。エラーが減ると修正時間も減り、高品質な製品の開発に時間をかけることができます。

トランクベースの開発手法

トランクベースの開発は単なるブランチ戦略ではなく、開発の効率化を実現する手法です。トランクベースの開発では、アプリを任意のタイミングでリリースできます。コードベースに未完成の部分が含まれる場合でも、様々なリリースタイプ(メジャーリリース、マイナーリリース、ホットフィックス、コード差分、改良など)を明確に区別しながらリリースすることが可能です。小さな変更を頻繁にトランクに直接コミットすることで、マージの衝突を少なくし、開発プロセスの早期の段階で処理することができます。機能トグルなどのリリース手法を使用すると、進行中の他の開発に影響を及ぼすことなく機能を個別にデリバリーできるため、開発プロセスの最後に発生しがちなマージの衝突を防ぐことができます。また、トランクベースの開発は、コードレビューを行ったりペアプログラミングモデルを採用したりすることでチーム内での知識共有が促進され、コラボレーションの強化につながります。また、トランクベースの開発では、CIサイクルの実行頻度が高まるなどフィードバックループも短縮されます。

API/UIのバージョン管理

バージョン管理は、APIやUIコンポーネントで互換性を破る変更(コアモジュール/プロデューサの変更)を行うにあたり、コンシューマに直接影響を及ぼしたくない場合など、特定のシナリオで役立ちます。この代表例としては、プロデューサの変更とコンシューマのリリースを同期できないケースや、(新しい変更が前の動作に影響しないことを確認する自動回帰テストがないことなどにより)リリースまでにコンシューマの品質を確認できていないケースが挙げられます。

API/UIのバージョン管理は、変更が特定のコード要素(新しい認証サービスや新しい製品カタログウィジェットなど)に限定され、(シグネチャや想定される動作の)互換性を破る変更が既存のコンシューマにすぐに影響を与えないようにしたいときに行いますまた、各コンシューマにとって新しいバージョンが妥当と判断したタイミングでも切り替えられます。

複数のバージョンがある場合、新しいバージョンを作成するタイミング、バージョンを非推奨にする方法、命名規則などの技術的な指針を明確にしておくことが適切なバージョン管理のアプローチとなります。バージョン管理方法の例については、「マイクロサービスのライフサイクル — バージョン管理」をご覧ください。

機能トグル

機能トグルはアプリケーションベースのリリース手法です。本番コードをデプロイすることなく機能の有効/無効を切り替え、システム動作を変更するための仕組みを備えています。一般的に機能トグルは、アプリケーションロジックやUI要素を条件文でラップして実装し、保存された構成設定に基づいて機能を有効化/無効化します。たとえば、新しい動作をエンドユーザーに段階的にロールアウト(カナリアリリース)できるビジネス機能トグルや、ユーザーに対して新しい動作と従来の動作の切り分けを行える機能フェンシングなどがあります。機能トグルはトランクベースの開発を可能にするコード手法であり、これによってパフォーマンスに優れた企業の特性である継続的デリバリーを実現します。State of DevOps Report 2019では、パフォーマンスに優れた企業はそうでない企業よりも高品質なソフトウェアを迅速にデリバリーすることができ、さらに本番環境でインシデントが発生してもすばやく復旧できることが指摘されています。

同じコードベースで複数の同時並行開発を行う場合、トランクベースの開発と機能トグルを併用すると、ブランチとマージを使用した開発よりもスピードや柔軟性が向上します。ただし、開発プロセスの複雑さ(トグル特有のコードや下位互換性の問題など)が増すほか、トグルを管理できるよう拡張性の高いアプローチを採用することが求められます。

技術的な機能トグルは、複数のコード要素(画面、ウィジェット、サービス、エンティティ)を使用するアプリケーションに機能や動作(新しいチェックアウトプロセスなど)を実装する際に、その作業が完了するまで他の開発者やテスターから作業を論理的に分離したい(つまり、他の開発者やテスターから新しい動作が見えないようにする)場合に適用します。機能トグルでは現在の動作と新しい動作の間で下位互換性が必要になるため、場合によってはAPI/UIのバージョン管理を使用する必要が生じます。また、データベースモデルの変更を段階的に行うことも必要になります。

機能フラグ/機能トグル

Feature Toggle Libraryコンポーネントは、OutSystemsコミュニティで取得できます。

抽象化によるブランチ

更新しようとしているモジュールやライブラリ、フレームワークにソフトウェアシステムの様々な部分が依存している場合、抽象化レイヤーを作成してクライアントコードの1つのセクションと現在のサプライヤーのインタラクションをキャプチャし、抽象化によりブランチを作成することができます。最初に、更新したいコンポーネントをラップするラッパーを作成します。これを使用すると、JIRAと連携するコネクタをAzure DevOpsと連携する別のコネクタに置き換える場合などに、古い実装から新しい実装へと呼び出しをルートさせることができます。これは、DLLのバージョンやCRMシステムを切り替える場合などのように、ユーザーのシグネチャ/インターフェイスは変えずに実装を大幅に変えたい場合にも向いています。抽象化によるブランチのメリットとして、リファクタリング中もコードを有効にしたままにして継続的デリバリーを実現できる点が挙げられます。

抽象化によるブランチの詳細な例については、「コアサービスを抽象化するための連携パターン」をご覧ください。この例では、外部システムをラップする抽象化レイヤーを作成しています。

品質の検証

継続的デリバリーの核となる原則のひとつが、「Build Quality In(品質の作り込み)」です。

米国の品質管理の権威であるW. Edwards Deming氏は次のように述べています。「問題や欠陥をすぐに見つけることができれば、修正コストが少なくてすみます。バージョン管理に入る前に、ローカルで自動テストを実行して見つけることができれば理想的です。[1]完了間際の検査(手動テストなど)で欠陥が見つかると、優先順位付けに大きな労力と時間がかかります。[2]しかも、問題が発生した数日前あるいは数週間も前のことを思い出しながら欠陥を修正しなければならないのです」。「Build Quality In」の原則とは、開発の早期の段階で欠陥を把握・修正することで保守コストを抑え、(本番移行時に)エンドユーザーに対して望ましくない影響が発生しないようにすることにほかなりません。開発規模が大きくなるほど、コードベースに欠陥(または技術的負債)が発生する確率も高まります。

OutSystemsは、早期の段階でのコード検証をしやすくし、コードのトレーサビリティを高めることで労力を減らす機能を提供することに努めています。優れたデプロイパイプラインには多数のフィードバックループが含まれます。パイプラインの各段階でテストが実行されます。テストに合格するとパイプラインが続行されますが、不合格であったり特定のしきい値を下回ったりするとパイプラインが停止し、チームがフィードバックに対応することになります。適切に設計されたパイプラインでは、フィードバックを高速で実行することで低品質なコードが本番に移行されることを防いでいるのです。

コード品質

TrueChange

TrueChangeはService Studioのビルトイン分析エンジンです。開発者にリアルタイムでフィードバックを提供し、警告を表示して無効なコードのパブリッシュを防ぎます。

Architecture Dashboard

Architecture Dashboardでは、アーキテクチャやパフォーマンス、セキュリティ、保守性に関する潜在的な問題を検出できます。保守性分析では、コメントのない複雑なコードや説明の不足している再利用可能な要素にフラグが表示されます。こうすることで適切に文書化されたコードが作成され、将来的に他の開発者がすばやく理解して改良できるようになります。Architecture Dashboardは技術的負債を検証し、高品質なコードの作成を促進することで、アプリケーションコードに問題が紛れ込むリスクを軽減します。

静的コード解析(自動)

OutSystemsコードを独立した形で解析する際(高度なセキュリティ監査がある場合など)は、Architecture DashboardとTrueChangeの補助として静的コード解析ツールを使用できます。こうしたツールはSIGBONCODEOmnextなどのサードパーティパートナーから入手できます。

コードレビュー(手動)

コードレビューにより、保守しやすいコードベースを作成し、フィードバックを迅速に収集することができます。コーディングのベストプラクティスや技術標準への準拠状況を確認するために、上級開発者やテックリードは定期的にコードレビューを行う必要があります。こうしたレビューでは、技術設計の検証とコードの確認を行い、ビジネス要件が満たされていることとアーキテクチャ設計に沿っていることを確かめます。コードレビューは他の開発者によって行われたり、コードの作成中にその場で行われたりする場合もあります。技術ソリューションとコードの検証を行うほか、コードがベストプラクティスに従っているかもチェックします。Architecture Dashboardではコード品質を検証するのに対し、コードレビューでは正確性を検証します。コードレビューは、コードの承認前に2人でチェックするという「ダブルチェックの原則」を実践するうえで最適な手法です。また、チームで責任を分担したり、チーム内で技術ソリューションの知識を共有したりする手段としても最適です。

機能品質

自動テスト

デプロイパイプラインで一番多いフィードバックループが、自動テストの実行です。回帰エラーテストなどの重要なプロセスを自動化することにより、チームがアプリケーションやソフトウェアの変更をユーザーにデリバリーする際のリードタイムを短縮することができます。また、アプリケーションをデプロイする際のスピード、信頼性、安全性も向上します。

自動テスト戦略を定義すると、ソフトウェアデリバリーを全体的に効率化し、ソフトウェア品質を安定させることができます。自動テストは人間が手動で行う場合と比べて格段に高速で実行できるため、システムが成長・拡大してもすばやくフィードバックを得ることができます。 また、ほぼ人手をわずらわすことなく、必要に応じて何回でも実行できます。初期設定時にコストはかかりますが、テスト作業に必要な時間を大幅に減らすことができます。そのため、検証の高速化、変更適用のリードタイムの短縮本番へのデプロイ頻度の増加が可能になります。さらに、自動テストを行うとデリバリー品質が確保され、アプリケーションの改善や本番移行時の問題の低減につながる実用的なデータも得ることができます。

自動化してアプリケーションコードが想定どおりに動作することを確認できる機能テストや回帰テストには多くの種類があります。回帰テストは、以前に受け入れられた動作をテストスコープ(単体、統合、E2E)にかかわらず検証するテストです。単体/コンポーネントテストでは、最も詳細なレベル(通常はアクション)でアプリケーションを検証します。短時間で実行できるうえに、保守もしやすく、アプリケーションの迅速な変更が可能です。単体テストは、自動化戦略の基盤となるべきテストです。回帰テストのコレクションでは、アプリケーション全体が変更後も想定どおりに動作することを検証します。このスイートはデプロイパイプラインの付加価値を高めます。

自動テストに関するガイドラインとツールの詳細については、「OutSystemsテストガイドライン」をご覧ください。

本番環境でのホットフィックス

ソフトウェアのリリースには、脆弱性や各種エラー、バグといった様々なリスクが付きものです。本番で重大なバグが見つかった場合、できるだけ早急に修正を行うことが必要になります。上記のプラクティスを実践している場合、本番でのホットフィックスが実質的に緊急修正となることがあります。これはあまり頻繁には発生せず、ビジネス部門からの新機能の緊急要請には該当しません(リリース頻度は数日単位や数週間単位ではなく数か月単位)。ただし、前述のプラクティスの実践状況に左右されるものであるため、万能な対処方法があるわけではありません。以下のトピックを考慮しながら、シナリオに応じて調整を加えるようにしてください。

リリース/デプロイ頻度

オンデマンドで(1日に複数回)本番に移行する場合、メインデリバリープロセスでホットフィックスをデリバリーできる場合があります。それ以外の場合は、具体的なホットフィックスプロセスを定めておくべきでしょう。

アプリケーションの重要度

本番に近い環境でアプリケーションの追加検証(ビジネス部門による受け入れ、プロセスのアップグレードのシミュレーション、負荷テストなど)を行う必要がある場合は、本番前環境でのホットフィックスプロセスが適しています。一方、重要度の低いアプリケーションでは、開発環境とQA環境を使用して評価を行い、ホットフィックスを作成・テストする方法を検討するのが一般的です。ただし、アプリケーションのアーキテクチャが適切であり、トランクベースの開発手法によって未完了の作業が隠されていることが前提条件となります。また、テスト範囲が適切に設定され、カナリアリリースが採用されているシナリオでは、重要なアプリケーションにもこの方法を適用できます。結局のところ大切なのは、リスクを管理し、適切な安全策を講じることです。

本番前環境でのホットフィックス

開発やテスト、デプロイと同様のデリバリー手順を使用する場合でも、異なるデリバリープロセスにおいては並行検証をサポートする追加の環境が必要になります。こうしたシナリオでは、開発手順とテスト手順をサポートする本番前環境が推奨されます。

  • この環境は本番環境と同じバージョンにする必要があります。

  • アプリケーションの重要度によっては、本番環境に近い環境を用意して、常にトラブルシューティングができるようにしてもよいでしょう。

  • この環境はメインパイプラインで以下の目的にも使用できます。

  • 本番移行前に行うデプロイのシミュレーション(ドライラン)。

  • 本番に近いデータを使用した、本番へのデプロイ前に行うトラブルシューティングやテスト。
  • ビジネスユーザーが手動テストで行うスモークテストや検証手順。
  • 負荷テストやパフォーマンステスト。

  • プロセスに必要な検証手順(手動テスト、回帰テスト)が含まれるようにします。

  • 適切なガバナンス(権限の管理)を確保します。

  • 修正をパイプラインの開始地点にマージします。

  • OutSystemsパイプラインの開始地点にまだマージされていないホットフィックスがLifeTimeで強調表示される。

  • ホットフィックスの実行時には責任の所在を明確にする。
  • 本番に修正をデプロイした後、保留になっているマージがないようにする。

そのほか、ロールバックプロセスの整備、カナリアリリース/段階的ロールアウトの適用、機能トグル使用時のキルスイッチ機能の実装なども検討する必要があります。

  • Was this article helpful?