TECH PLAY

テスト

イベント

マガジン

技術ブログ

本記事は 2026 年 3 月 6 日に公開された “ AWS Load Balancer Controller adds general availability support for Kubernetes Gateway API ” を翻訳したものです。 AWS は最近、 Amazon Web Services (AWS) Load Balancer Controller による Kubernetes Gateway API サポートの一般提供を発表しました。これまで、AWS Load Balancer Controller は Kubernetes Ingress と Service リソースの要件を満たすため、それぞれ Application Load Balancer (ALB) と Network Load Balancer (NLB) をプロビジョニングしていました。この新機能により、標準の Kubernetes Gateway API を使用してAWSロードバランシング機能を定義できるようになりました。この投稿では、AWS Load Balancer Controller の Kubernetes Gateway API サポートをご紹介します。Gateway API 統合の構成ベストプラクティス、機能、ユースケースを共有し、AWS 上での Kubernetes デプロイメントのモダナイゼーションと合理化を支援します。 前提条件 Kubernetes API、コントローラー、Deployments、Services、カスタムリソースなどのリソースタイプを含む Kubernetes の概念に精通していることを前提としています。また、 Amazon Elastic Kubernetes Service (Amazon EKS) と AWS ロードバランシングサービスについての基本的な理解も必要です。これらの概念を復習する必要がある場合は、Kubernetes ドキュメントと Amazon EKS ユーザーガイドから始めることをお勧めします。 Gateway API 概要 Gateway API は、Kubernetes クラスターで L4 (TCP/UDP) および L7 (HTTP/gRPC) トラフィックルーティングを管理するための次世代フレームワークです。従来の Ingress API よりも柔軟で表現力豊かなロール指向のアプローチを提供し、Ingress における以前の制限に対処しています。流入トラフィック (north-south トラフィック) やサービス間接続 (east-west トラフィック) を含む、幅広いトラフィック管理ニーズに対応する標準化された、ポータブルで拡張可能な仕様を提供します。 Ingress API は長年にわたって Kubernetes ユーザーによく貢献してきましたが、Gateway API が対処するいくつかの制限があります。Ingress は高度な機能に対してベンダー固有のアノテーションに依存しており、異なる実装間での構成の移植性を低下させています。さらに、ロールベースのリソース管理のネイティブサポートが不足しており、これはクラスター運用者とアプリケーション開発者が同じ構成面を共有することを意味します。Gateway API は、インフラストラクチャプロバイダー (GatewayClass)、クラスター運用者 (Gateway)、アプリケーション開発者 (Routes) に対して異なるリソースを持つロール指向設計を導入しています。この分離により、より良い組織ポリシーとセキュリティ境界が可能になります。さらに、Gateway API は、クロス名前空間ルーティング、重み付きトラフィック分割、ヘッダーベースルーティング、L4 と L7 の両方のプロトコルに対するネイティブサポートを提供します。これらの機能は以前は複雑な回避策が必要であったか、Ingress だけでは不可能でした。これらの改善により、Gateway API はより表現力豊かで拡張可能であり、現代のクラウドアーキテクチャに適したものになります。 AWS のフルマネージドアプリケーションネットワーキングサービスである Amazon VPC Lattice は、 Amazon VPC Lattice Gateway API Controller を通じて Kubernetes Gateway API のサポートを既に提供しています。これは、VPC とアカウント内の EKS クラスター間でサービス間接続を可能にする別のコントローラーです。AWS Load Balancer コントローラーも Gateway API 仕様をサポートすることで、ALB と NLB による流入 (north-south)トラフィック管理と、east-west サービスメッシュ機能に VPC Lattice を使用する柔軟性の両方について包括的なカバレッジを得ることができます。そして、これらはすべて同じ標準化された Gateway API リソースを使用して実現されます。 Gateway API コンポーネント Gateway API は、Kubernetes クラスターへのトラフィックの流入と通過の方法を定義するために連携する 3 つのコアリソースを導入します。これらのコンポーネントとそれらが AWS インフラストラクチャにどのようにマッピングされるかを理解することで効果的なトラフィック管理戦略を設計できます。図 1 は Gateway API 仕様のコンポーネントを示しています: 図 1 : Gateway API コントローラーコンポーネント [ 出典 ] GatewayClass GatewayClass は、共通の構成と動作を持つ Gateway を作成するためのテンプレートを定義します。プラットフォームチームは GatewayClass を使用して組織ポリシーとインフラストラクチャテンプレートを確立します。例えば、GatewayClass はどのコントローラーが Gateway を管理するか (AWS Load Balancer Controller など) と、どのタイプのロードバランサーをプロビジョニングするか (ALB または NLB) を指定します。 以下は、ALB をプロビジョニングする GatewayClass の例です: apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: aws-alb spec: controllerName: gateway.k8s.aws/alb Gateway Gateway はロードバランサーをインスタンス化し、トラフィックがクラスターにどのように入るかを定義します。クラスター運用者は、リスナー (プロトコルとポートの組み合わせ)、ホスト名、TLS 設定を指定するように Gateway を構成します。Gateway を作成すると、AWS Load Balancer Controller は指定されたリスナーで対応する AWS ロードバランサーをプロビジョニングします。 以下は、HTTP と HTTPS リスナーを持つ ALB を作成する Gateway の例です: apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: my-gateway namespace: default spec: gatewayClassName: aws-alb listeners: - name: http protocol: HTTP port: 80 - name: https protocol: HTTPS port: 443 hostname: example.com Routes Routes は、リスナーをバックエンド Kubernetes サービスにマッピングするルーティングルールを定義します。アプリケーション開発者は、パス、ヘッダー、ホスト名などのリクエスト属性に基づいてトラフィックがサービスにどのように分散されるかを制御するために Routes (HTTPRoute、GRPCRoute、TCPRoute、UDPRoute、TLSRoute) を作成します。 以下は、Gateway の HTTP リスナーから Kubernetes サービスにトラフィックをルーティングする HTTPRoute の例です: apiVersion: gateway.networking.k8s.io/v1beta1 kind: HTTPRoute metadata: name: my-route namespace: default spec: parentRefs: - name: my-gateway sectionName: http rules: - matches: - path: type: PathPrefix value: /app backendRefs: - name: my-service port: 8080 AWS Load Balancer Controller は Kubernetes Gateway API オブジェクトの調整をサポートしています。以下を満たします: TCPRoute や UDPRoute などの L4 ルートを AWS NLB で HTTPRoute や GRPCRoute などの L7 ルートを AWS ALB で 動作の仕組み AWS Load Balancer Controller が Gateway API リソースを AWS インフラストラクチャにどのように変換するかを理解することで、より信頼性の高いアプリケーションを設計し、問題をより迅速にトラブルシューティングできます。コントローラーの調整モデルにより、Kubernetes の定義が AWS ロードバランサーと同期された状態を保ち、継続的な検証とリアルタイムステータスフィードバックを提供します。 高レベルでは、以下のステップが調整ループの動作を示しています: API 監視 : Gateway API リソースの作成、変更、削除について、Kubernetes API が継続的に監視されます。 キューイング : コントローラーは特定されたリソースを処理のための内部キューに追加します。 処理 : 内部キューの各アイテムに対して: コントローラーは関連する GatewayClass をチェックして、ユーザーが AWS ロードバランサーを要求しているかどうかを確認します。 yes の場合、対応する Gateway リソースの Gateway API 定義が、NLB/ALB、リスナー、リスナールール、ターゲットグループ、またはアドオンなどの AWS リソースにマッピングされます。 これらのマッピングされたリソースは、AWS の実際の状態と比較されます。差分があると、コントローラーは AWS の状態を Gateway オブジェクトによって設定された望ましい状態と一致させます。 ステータス更新 : 調整後、AWS Load Balancer Controller は対応する Gateway および Route リソースのステータスフィールドを更新します。これにより、ALB の DNS 名や Amazon Resource Name (ARN) などのプロビジョニングされた AWS リソース、およびデバッグを支援するエラー詳細について、リアルタイムフィードバックが提供されます。例えば、HTTPRoute を作成すると、ステータスフィールドにはルートが受け入れられたかどうか、それが接続されている Gateway、およびプロビジョニング時にトラフィックルーティングに使用する ALB の DNS 名がすぐに表示されます。無効な証明書 ARN などの構成エラーがある場合、ステータスフィールドは実用的なエラーメッセージを提供します。 図 2 は、この調整フローを詳細に示しています。コントローラーは Gateway API リソースに対するウォッチを確立し、内部キューを通じて変更を処理し、望ましい状態の中間表現を構築し、対応する AWS リソースを実現します。このプロセス全体を通じて、プロビジョニングの進行状況と発生したエラーを反映するためにステータス条件が更新されます。 図 2 : Gateway API コントローラー調整フロー この宣言的アプローチは、望ましいロードバランシング構成を一度定義すると、コントローラーが AWS がその意図と一致することを継続的に保証することを意味します。これにより、ドリフトと構成変更が自動的に処理されます。 主要機能 このセクションでは、Load Balancer Controller Gateway API サポートの主要機能について説明します: 強化されたカスタマイズ AWS Load Balancer Controller は、適切に定義された Kubernetes Custom Resource Definitions (CRD) を採用し、アノテーションベースの構成を廃止します。この変更により、検証や IDE サポートが不足しているアノテーション文字列に複雑なデータ構造を埋め込むという制限に対処します。例えば: Before (アノテーションベースアプローチ): apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-ingress annotations: alb.ingress.kubernetes.io/target-group-attributes: | deregistration_delay.timeout_seconds=30, stickiness.enabled=true, stickiness.type=lb_cookie After (CRDベースアプローチ): apiVersion: gateway.k8s.aws/v1beta1 kind: TargetGroupConfiguration metadata: name: my-app-tg-config namespace: my-app spec: targetReference: name: my-service defaultConfiguration: targetGroupAttributes: - key: deregistration_delay.timeout_seconds value: "30" - key: stickiness.enabled value: "true" - key: stickiness.type value: lb_cookie healthCheckConfig: healthyThresholdCount: 5 healthCheckInterval: 30 healthCheckPath: /health healthCheckPort: "8080" healthCheckProtocol: HTTP healthCheckTimeout: 5 unhealthyThresholdCount: 2 AWS Load Balancer Controller は Gateway API カスタマイズのために 3 つの CRD を導入しています: TargetGroupConfiguration : サービスレベルでヘルスチェック、登録解除遅延、スティッキネス設定を含むターゲットグループプロパティを構成 LoadBalancerConfiguration : Gateway または GatewayClass レベルでサブネット配置、セキュリティグループ、アクセスログなどのリスナーおよびロードバランサープロパティを定義 ListenerRuleConfiguration : Amazon Cognito または OIDC プロバイダーを通じた認証を含む AWS 固有のルーティング動作で HTTPRoute および GRPCRoute リソースを拡張 これらの CRD は、スキーマ検証、タイプセーフティ、GitOps ワークフローとのネイティブ統合を提供します。構成エラーは実行時ではなく適用時に表面化し、運用オーバーヘッドを削減し、インフラストラクチャの信頼性を向上させます。 Cross-Namespace トラフィックルーティング Gateway API は、Cross-Namespace ルーティングを通じてインフラストラクチャ運用者とアプリケーション開発者の間の関心の分離を可能にします。プラットフォームチームは共有 Gateway リソースをプロビジョニングし、アプリケーションチームはクラスターレベルの権限を必要とせずに独自の Rout を管理します。 このモデルでは、プラットフォームチームがアクセス制御された専用の名前空間に Gateway をデプロイします。別々の名前空間のアプリケーションチームは、共有 Gateway を参照する Route リソースを作成します。コントローラーは、これらの分散された定義に基づいてトラフィックをルーティングするようにロードバランサーを自動的に構成します。 このアーキテクチャは、Kubernetes API レベルで RBAC 境界を強制します。アプリケーションチームは、サブネット配置やセキュリティグループ割り当てなどのインフラストラクチャレベルの構成にアクセスすることなく、サービスのルーティングロジックを管理します。 自動 TLS 証明書検出 AWS Load Balancer Controller は、L4 と L7 の両方のプロトコルについて、Ingress API から Gateway API リスナーへの証明書検出を拡張します。Gateway リスナーまたは Route ホスト名でホスト名が指定されると、コントローラーは AWS Certificate Manager (ACM) にクエリを実行して、ホスト名マッチングに基づいて一致する証明書を見つけて添付します。 コントローラーは証明書の更新について ACM を監視し、ロードバランサー構成に変更を自動的に適用します。これにより、手動での証明書 ARN 管理が不要になり、複数の環境間での証明書ローテーションの運用複雑性が軽減されます。 これらの機能により運用オーバーヘッドを削減し、関心の分離を改善した本格的な AWS 上の Kubernetes ネットワーキングが可能になります。型安全な CRD、Cross-Namespace ルーティング、自動証明書管理の組み合わせにより、チームはインフラストラクチャ構成ではなくアプリケーション配信に集中できます。 始め方 初回ユーザーの場合は、インストールガイドに従ってください。このガイドでは、必要な AWS Identity and Access Management (IAM) 権限を設定し、AWS Load Balancer Controller を Kubernetes クラスターにデプロイします。 AWS Load Balancer Controller で Gateway API サポートを有効にするには、まず 前提条件 が満たされていることを確認してください。その後、Gateway API サポートを有効にするには、 こちら で説明されているように機能フラグを有効にする必要があります。 構成例 この例では、AWS Load Balancer Controller Gateway API 統合の 3 つの主要機能を実証します : HTTP ルーティング、自動証明書検出を使用した HTTPS ルーティング、ListenerRuleConfiguration を使用したカスタムルーティングルール。デプロイメント手順と検証コマンドを含む完全なウォークスルーについては、AWS Load Balancer Controller Gateway API の例を参照してください。 この例では、以下のアーキテクチャでALBをプロビジョニングします: ポート 80 の HTTP リスナーが blue デプロイメントにルーティング ポート 443 の HTTPS リスナーが自動証明書検出により orange デプロイメントにルーティング HTTP リスナー上の /alb-response パスに対して固定レスポンスを返すカスタムルーティングルール 構成概要 構成には以下が含まれます: AWS Load Balancer Controller を実装として定義する GatewayClass : # alb-gatewayclass.yaml apiVersion: gateway.networking.k8s.io/v1beta1 kind: GatewayClass metadata: name: aws-alb-gateway-class spec: controllerName: gateway.k8s.aws/alb HTTP および HTTPS リスナーを持つ Gateway : # my-alb-gateway.yaml apiVersion: gateway.networking.k8s.io/v1beta1 kind: Gateway metadata: name: my-alb-gateway namespace: example-ns spec: gatewayClassName: aws-alb-gateway-class infrastructure: parametersRef: kind: LoadBalancerConfiguration name: lbconfig-gateway group: gateway.k8s.aws listeners: - name: http protocol: HTTP port: 80 allowedRoutes: namespaces: from: Same - name: https hostname: "sample.com" protocol: HTTPS port: 443 allowedRoutes: namespaces: from: Same スキームまたはその他のゲートウェイ構成パラメータ用の LoadBalancerConfiguration : # lb-config-gateway.yaml apiVersion: gateway.k8s.aws/v1beta1 kind: LoadBalancerConfiguration metadata: name: lbconfig-gateway namespace: example-ns spec: loadBalancerName: "my-example-gateway-alb" scheme: internet-facing カスタムルーティング動作用の ListenerRuleConfiguration : # listener-rule-config-blue.yaml apiVersion: gateway.k8s.aws/v1beta1 kind: ListenerRuleConfiguration metadata: name: blue-lrc-config namespace: example-ns spec: actions: - type: "fixed-response" fixedResponseConfig: statusCode: 404 contentType: "text/plain" messageBody: "customized text" リスナーをバックエンドサービスにマッピングする HTTPRoutes : # httproute-blue.yaml apiVersion: gateway.networking.k8s.io/v1beta1 kind: HTTPRoute metadata: name: http-route-blue namespace: example-ns spec: parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: my-alb-gateway sectionName: http rules: - matches: - path: value: "/alb-response" filters: - type: ExtensionRef extensionRef: group: "gateway.k8s.aws" kind: "ListenerRuleConfiguration" name: "blue-lrc-config" - backendRefs: - name: service-blue port: 80 # httproute-orange.yaml apiVersion: gateway.networking.k8s.io/v1beta1 kind: HTTPRoute metadata: name: http-route-orange namespace: example-ns spec: parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: my-alb-gateway sectionName: https rules: - backendRefs: - name: service-orange port: 80 デプロイとテスト この例をデプロイするには、バックエンドの DeploymentsとServices が必要です。以下のリソースを作成してください: # deployment-orange.yaml apiVersion: apps/v1 kind: Deployment metadata: name: deployment-orange namespace: example-ns spec: selector: matchLabels: app: orange-app replicas: 1 template: metadata: labels: app: orange-app spec: containers: - image: k8s.gcr.io/e2e-test-images/echoserver:2.5 imagePullPolicy: Always name: echoserver ports: - containerPort: 8080 --- # deployment-blue.yaml apiVersion: apps/v1 kind: Deployment metadata: name: deployment-blue namespace: example-ns spec: selector: matchLabels: app: blue-app replicas: 1 template: metadata: labels: app: blue-app spec: containers: - image: k8s.gcr.io/e2e-test-images/echoserver:2.5 imagePullPolicy: Always name: echoserver ports: - containerPort: 8080 --- # service-orange.yaml apiVersion: v1 kind: Service metadata: name: service-orange namespace: example-ns spec: ports: - port: 80 targetPort: 8080 protocol: TCP type: NodePort selector: app: orange-app --- # service-blue.yaml apiVersion: v1 kind: Service metadata: name: service-blue namespace: example-ns spec: ports: - port: 80 targetPort: 8080 protocol: TCP type: ClusterIP selector: app: blue-app Gateway がプロビジョニングされるまで待機します: kubectl wait --for=condition=Programmed gateway/my-alb-gateway -n example-ns デプロイメントの検証 blue デプロイメントへの HTTP ルーティングをテストします: # ALB ホスト名を取得 ALB_HOSTNAME=$(kubectl get gateway my-alb-gateway \ -n example-ns \ -o jsonpath='{.status.addresses[0].value}') # HTTP エンドポイントをテスト curl http://$ALB_HOSTNAME orange デプロイメントへの証明書検出を使用した HTTPS ルーティングをテストします: curl -k https://$ALB_HOSTNAME -H "Host: sample.com" カスタムルーティングルールをテストします: curl http://$ALB_HOSTNAME/alb-response NLB 構成、Cross-Namespace ルーティング、高度なカスタマイゼーションシナリオを含むその他の例については、AWS Load Balancer Controller ドキュメントを参照してください。 考慮事項 GatewayClass によるポリシー強制 : プラットフォームチームは、サブネット配置、セキュリティグループ割り当て、アクセスログを制御する LoadBalancerConfiguration 設定を持つ GatewayClass リソースを定義します。アプリケーションチームは、インフラストラクチャ変更権限なしに承認された GatewayClass オプションから選択します。これにより、 Kubernetes RBAC を通じて内部ワークロードをインターネット向けロードバランサーから分離するなどの組織ポリシーが強制されます。 標準ベースの移植性 : Gateway、GatewayClass、Route リソースは Kubernetes Gateway API 仕様に従います。コアルーティングロジックは実装間で一貫性を保ちます。AWS 固有の CRD (LoadBalancerConfiguration、TargetGroupConfiguration、ListenerRuleConfiguration) はオプションのままで、コアルーティング定義から分離されています。これにより、必要に応じてAWS固有の機能を持つポータブルなアプリケーション構成が可能になります。 運用可視性 : コントローラーは、リアルタイムプロビジョニング進行状況、リソース識別子 (ロードバランサー ARN と DNS 名)、エラーメッセージで Gateway API ステータスフィールドを更新します。これにより、トラブルシューティング時間が短縮され、リソース状態を確認するための直接的な AWS API クエリが不要になります。 AWS サービス統合 : この実装は、自動証明書検出のための ACM、アプリケーション保護のための AWS WAF 、DDoS 保護のための AWS Shield Advanced と統合されます。ターゲットグループ構成、ヘルスチェック、アクセスログは Amazon S3 および Amazon CloudWatch と統合されます。 GitOps 互換性 : スキーマ検証を持つ型安全な CRDは、デプロイ前検証中に構成エラーを表面化します。Infrastructure as Code (IaC) ツールである Flux 、 ArgoCD 、Kubernetes 用 AWS Cloud Development Kit (AWS CDK) は、自動ドリフト検出を伴う宣言的インフラストラクチャ管理のための Gateway API サポートを提供します。 まとめ AWS Load Balancer Controller の Gateway API サポートは、AWS 上での Kubernetes トラフィック管理に対する標準ベースのアプローチを提供します。Gateway API 仕様を採用することで、Kubernetes 環境間での移植性を維持しながら、ロール指向のリソース管理、Cross-Namespace ルーティング機能、型安全な CRD による拡張されたカスタマイゼーションを得ることができます。プラットフォームチームが Ingress ベースの構成から移行する場合でも、新しい Kubernetes デプロイメントを構築する場合でも、Gateway API 統合によってアプリケーションチームにセルフサービスルーティング機能を提供しながら組織ポリシーを強制することができます。この実装は、証明書管理、セキュリティ、可観測性のためのネイティブ AWS 統合と組み合わせることで運用ワークフローを合理化し、構成の複雑性を軽減します。AWS Load Balancer Controller デプロイメントで Gateway API サポートを有効にして、今すぐ始めましょう。詳細な構成例とベストプラクティスについては、AWS Load Balancer Controller ドキュメントを参照してください。
はじめに こんにちは、株式会社スタメン 東京プロダクト組織のエンジニアリングマネージャー まっきーです。先日、 EMConf 2026 に参加してきました。EMとして日々悩みながら手を動かしている身として、組織のあり方・マネジメントの考え方・技術と人の接点まで、自分に刺さるセッションが多いカンファレンスでした。 今回の記事は、印象的だったセッションと、参加して得られた気付きを参加レポートとしてまとめたものになります。 冒険する組織のつくりかた 2026.emconf.jp 「軍事的マネジメント」から「冒険的マネジメント」へのパラダイムシフト の話でした。 マネジメントという概念が生まれたのは1900年代、工場の生産ラインの安定化が目的でした。その延長で「兵隊の訓練」がフレームワークになり、目標の達成・統制・危機感の醸成といった発想が根付いていきました。一方で、ここ2〜30年で 会社中心から人生中心のキャリア観 へと世の中が変わり、1980年代型の「危機感を煽る」やり方は人材流動性の高い今では通用しなくなっているという話でした。 そこで提案されていたのが、 半径5mからできる4つのマネジメント です。 ① 目標:SMART と ALIVE の両立 軍事的マネジメントは「目標に対する視野狭窄」を生みがち。一方、冒険できる組織では、 SMART (Specific, Measurable, Achievable, Relevant, Time-bound) ALIVE (Adaptive, Learningful, Interesting, Visionary, Experimental) の両立が重要だという話でした。チームで「ALIVE を達成できているか」を問い直す時間を作ることで、明確な目標設定をすることができそうだと感じました。 組織づくりは、組織に張り巡らされている「目標設定」の網の質を変えていくところから。 最近は具体的な目標を立てる際には従来の"SMART"に加えて"ALIVE"になっているかどうかを意識しています。 #154 SMARTに代わる、目標設定の新法則「ALIVE」 - 安斎 勇樹 https://t.co/EgGPba9qnm #Voicy pic.twitter.com/S78SSI757c — 安斎勇樹 / 最新刊『静かな時間の使い方』予約受付中 (@YukiAnzai) 2024年9月10日 ② 会議:問いの質 「ミーティングで反応が悪いのは、メンバーのせいではなく、ファシリテーターの問いが悪い」 という話を聞き、自分がファシリテーターの時のミーティングは時々こうなっていることに気付きました。対策として、選択肢を絞るような問いにすることで、議論が動きやすくなるとのことでした。これについてはカンファレンス参加後のミーティングから意識するようにしています。 ③ 興味:好奇心と興味の違い 好奇心 :瞬発的な感情 興味 :好奇心が継続し、一定の対象に定着している AI時代には散漫な好奇心より、 深い興味 が重要。 興味 =「レンズ(どんなものの見方で)」×「対象(なにを見るか)」 という表現をされていました。 数年後のビジョンをいきなり立てるより、「いま何が面白いか・興味があるか」を大事にする方という考え方は、目標を立てるのが苦手な自分には少し肩の荷が降りるものでした。 ④ 文化:風土と文化は違う 風土の改善だけでなく、 文化を耕す 。価値基準の集合体が文化であり、皆が集まる場で繰り返し伝え、刷り込み続ける。耕し続ける姿がなければ、マネージャーの「本気」も伝わりにくく、共感も生まれにくいという話は、マネージャーとしての立ち位置を改めて認識させられるものでした。 自律型組織の真実:『甘い自走』を捨てて導いた、EMによる戦略的組織変革 「自走」という言葉が一人歩きし、 マネジメント放棄 に近い状態になってしまうケースについてのセッションでした。 甘い自走の結果として、 生産性低下・士気低下 技術的負債の放置 現場の混乱がそのまま事業の停滞に直結 といった問題が起きます。そして「目的とゴールを浸透させる」「評価制度を見直す」「1on1を増やす」といった 重い仕組みの追加 では、組織が死んでしまうだけ。 重要なのは、 自律 ≠ 自由 自律 = 「境界線(ガードレール)」と「責任」のセット 「ここまではノーブレーキでOK、ここからは止まれ」を決める。最もレバレッジが効くのは「意思決定」に集中する。意思決定ができると、ドミノ倒しのように仕事が進んでいく、という話でした。 この話を聞いて、まず自分自身が仕事に対して責任範囲をきっちりと決めきれていないことに気付きました。そんな状態では、メンバーに対して自律して動いてくださいなどと言えるはずもありません。 メンバー目線で見ても境界線を定めてくれるマネージャーは間違いなく信頼できるので、今後身につけていきたいスキルだと強く感じました。 「ストレッチゾーンに挑戦し続ける」ことって難しくないですか? メンバーの持続的成長を支えるEMの環境設計 在籍歴の長いエンジニアの退職申し出をきっかけに、 ストレッチゾーンのチャレンジが枯渇してしまう 問題にどう向き合うかを扱ったセッションでした。 「今の自分に最適なチャレンジが分からない」という状態に対して、 will / can / must で現状分析する。 ただし、自分の will を正しく言語化できるとは限らないため、must と噛み合わずアクションが生まれないことがある。 そこで、will を一度抽象化(例:レガシーコードを刷新したい → 意思決定がしたい)してから具体化(テストカバレッジ改善など)する また、目標設定に失敗してしまうパターンとして - 経営がうまくいっている企業では、分かりやすいストレッチな目標が減り、インパクトの弱い仕事を選んでしまう -「退屈だが自分の価値が出せる仕事」は魅力的で選びがちだが、野心が削がれ、低い目標設定につながる - 忙しさを理由に「できなかった」を発生させてしまう という話があり、目標を立てるのが苦手な自分にはかなり耳の痛い話でした。 これらへの対策として、 メンバー間の 目標の可視化 裁量範囲の言語化 と権限委譲(「どこまでやっていいか分からない」をなくす) span of control の上限 を意識し、マネージャーがボトルネックにならない仕組み が挙げられていました。 組織・文化・技術の壁に挫折したEMが、アーキテクトとして「構造化思考」を手に、再び保守開発組織の変革に取り組む ピープルマネジメントに注力すればよいと思っていたが、 コード品質の悪さとチームの疲弊 という構造的な問題に直面し、仕様やテストの重要性を説くも場当たり的対応に終わり、撤退を選んだ経験から、EM がアーキテクトとして「構造化思考」を手に再挑戦する話でした。 また、 AI は増幅器である というお話もありました。属人的プロセスやレガシーを放置したまま AI を導入すると、カオスと技術的負債が爆発する。是正すべきは次の3つの「モデルのずれ」です。 How 領域のずれ 実装工程を AI に任せるなら、AI がコードを実装するワークフローをエンジニアリングする。 What/Why 領域のずれ 要求を整合させ、価値あるものを創るために、要求の3階層(ステークホルダ要求・ビジネス要求・ソリューション要求)を意識する。 組織境界とシステム間のずれ 境界を見極めてモジュールとして切り出す。 とにかく思いついたら図にして共有する 変化を起こすには、 組織・チームの目標に据えなければ「余力で進める」からは逃れられない 。EM が本気で取り組まなければ進まない。属人的でその場しのぎの対応に終わらせない。「EMはビジネス・システム・チーム・技術の関係(構造)をデザインせよ」は常に心に留めておきたいと思いました。 まとめ EM歴9ヶ月の状態で参加した今回のEMConfでしたが、参加前までは開発組織の抱える課題に対して、EMという立場からどのように立ち向かっていけばよいか分からない状態でした。今回様々な知見を得られたことで、そういったぼんやりとした状態から少し脱することができ、また自身がEMとしてやりきれていない事が浮き彫りになりました。特に、「自律して動いており開発生産性の高い組織」とはどのようなものかについては、セッションごとに共通しているテーマがあり、私自身としては「権限委譲が適切にできていること」「目標がはっきりしていること」だと感じました。 直近でAIによりソフトウェア開発の根本が全く変わってしまったこと、スタメン内部の開発組織の構造も大きく変革が進む中で、いかに組織の課題解決のために時間を使っていけるかが、組織・自分自身ともに勝負だと感じたので、今回のカンファレンスで得られた知見を具体的なアクションに落としこんでいければと思います。 herp.careers
目次 目次 0. はじめに 1. レビューされる側だった頃の問題点 2. レビューと設計の関係性 2.1 なぜレビューが必要なのか 2.2 なぜ「設計」が関係するのか 3. コードレビュー指摘の傾向から学んだこと 3.1. 様々な設計原則 3.2. 設計指摘を具体的に理解する 3.3. 設計指摘をものにするためには? 4. before / after で見る設計指摘の具体例 4.1. SRP: 「責務が多い」と言われたケース before: 1つのクラスに複数の責務がある after: 責務ごとに分離する 4.2. OCP: 「将来増えそう」と言われたケース before: 条件分岐で処理を切り替えている after: 振る舞いを分離する 4.3. DRY: 「共通化できそう」と言われたケース before: 同じコードが複数箇所にある after 其ノ壱: 知識を一箇所に集約 after 其ノ弐: 処理を一箇所に集約 4.4. before / after から学んだこと 5. 実装時に持つべき視点 6. 同じ立場の人たちへ 7. まとめ 0. はじめに はじめまして。新卒一年目の楽楽販売開発エンジニアです。 ほとんど開発経験がない状態で入社し、数ヶ月の研修を経て実務に入りましたが、最初はコードレビューで受けた指摘の意図や直し方が分からず戸惑うことばかりでした。 例えば、コードレビューでこんな指摘を受けたことはないでしょうか。 「このクラス、責務が多くないですか?」 「将来 if が増えそうですね」 「共通の処理が他にもあります」 私はまさにこのようなコードの設計に関する指摘をたくさん受けてきました。 ただ当時は「これらはなぜ必要なのか?結局どう直せばいいのか?」と思っていました。 私が書いたコードは要件通りに動くし、これらの指摘はどこか抽象的で、実装とどう結びついているのかが分からなかったからです。 それでもレビューで指摘を受けては実装を直し、学習を重ねるうちに、次第に なぜその指摘が出てくるのか どこを直せばよいのか が、以前よりもはっきりしてきました。そこから実装やレビューの時に意識するべきポイントを見出すことができました。 この記事ではコード設計に関する指摘に焦点を当て、これまでの私の変化と学びを元に 実装やレビューの時に気をつけると良いこと について述べます。 ここで紹介されるポイントを理解し実践することで、実装やレビューの質を上げていってほしいです。 コーディング経験が浅い人やこれからレビューを受ける人、これからレビューを始める人の参考になれば嬉しいです。 1. レビューされる側だった頃の問題点 最初の頃の私は、個人でコードを書いている感覚のまま、チーム開発をしていました。 とりあえず要件を満たして動けばOK 保守性・可読性は「なんとなく」意識する程度 それに加えて重要な設計原則を知らず、気にしていなかったです。 このような状態だったので、レビューで多くの指摘を受けていました。 今だからこそ言えることですが、以下のような具体的な観点を実装時に持っていなかったことが原因の1つでした。 どこまでを1つの責務と考えるのか 将来どんな変更が入りそうか 他の人が読んだときに意図が伝わるか いつも一貫した考えのもとで実装をしていなかったため、「指摘されてはその場で修正する」の繰り返しでした。 つまり、 再現性のある判断基準を持てていなかった のです。 なお、上記のような観点の詳細やその重要性については 3 節以降で述べます。 2. レビューと設計の関係性 2.1 なぜレビューが必要なのか ここで「レビューの必要性」について少し述べておきます。当初の私は、「レビューはバグを見つけるためのもの」だと考えていましたが、実際は違いました。 一般に、バグを見つけるのはテストでできるからこそ、中長期的な品質や保守性を担保するためにレビューを行います。 チーム開発には、 自分以外の人がコードを読む 複数人で継続的に機能追加や修正を行う という特徴があるため、できるだけ早い段階で複雑さが増えにくい形にコードを整えておくことが重要になります。 これを実現するためにレビューが必要なのです。 つまりレビューは、 「今の正しさ」だけでなく「将来も扱いやすいか」 を確認するためにあると言えます。 また、結果としてそれは お客様に価値を早く、継続的に届けるための投資 になります。 2.2 なぜ「設計」が関係するのか レビューが「将来の扱いやすさ」を見る場だとすると、「では、その扱いやすさは何で決まるのか?」と疑問に感じるでしょう。 そこで出てくるのが「コード設計」です。 責務の分け方 変更理由の整理 依存関係の持ち方 etc. レビューでコード設計の話が出てくるのは、それが中長期的な保守性を大きく左右するからです。 3. コードレビュー指摘の傾向から学んだこと ここではレビューで多かった指摘を振り返り、設計原則との関係を見ていきます。 また「コード設計に関する指摘」をもう少し具体的にします。 当時よくもらっていたのは、例えば次のような指摘です。 責務が多い 同じような処理が増えていきそう 同じような処理が他にもある 変更時の影響範囲が広そう これらを別々の指摘として受け取っていた当初は、「なぜ指摘されるのか」「どうすれば指摘されなくなるか」を根本から理解できていませんでした。 後から整理すると、表現は違えど、これらはどれも 将来の変更に弱い ことを指摘していました。 これがまさに将来の扱いやすさに言及する「コード設計に関する指摘」(以降「設計指摘」)だったのです。 したがって、設計指摘とは「将来困りそうか?」を様々な観点から判断してなされる指摘だと言えます。 3.1. 様々な設計原則 設計指摘を具体的に理解するために、まずは私が受けた設計指摘に大いに関係していた設計原則を3つ紹介します。 SRP(Single Responsibility Principle: 単一責任原則) 1つのクラスやメソッドには1つの責務のみを与えるべし OCP(Open-Closed Principle: 開放閉鎖原則) 既存コードの修正なしで拡張可能にするべし DRY(Don't Repeat Yourself) 重複を排除するべし 設計原則はどれも、将来の変更に対して壊れにくく、修正しやすく、ミスが入りにくい構造をあらかじめ作るための考え方です。 つまり、レビュワーが考えることはまさに設計原則そのものなのです。 それゆえ、レビューでは設計原則に基づいた指摘、すなわち設計指摘が頻出します。 3.2. 設計指摘を具体的に理解する 設計原則を知るともらった指摘の根底にある考えが分かるので、それらをより具体的に理解できます。 例えば、 「責務が多い」 → SRP の観点で、変更理由が増えそう 「同じような処理が増えそう」 → OCP / DRY の観点で、修正漏れが起きそう 「影響範囲が広い」 → 依存関係が整理されておらず、防御しにくい。あるいは DRY を守れていない こうして、当時は抽象的に思えた指摘をやや具体的に理解することができました。 3.3. 設計指摘をものにするためには? 設計指摘の根底にある考えは「このままでは将来が不安である」というものでした。 これだと抽象的に思えますが、実はある程度定まった基準が存在しており、それが設計原則でした。 設計原則を知り、実際のコードに応用できるようになれば、設計指摘で悩むことはなくなると思っています。 そういうわけで、次節では設計指摘を受けるコードとはどんなものなのかを見ていきます。 設計指摘を受ける理由やどのように直せば良いのかをまとめ、更に理解を進めます。 4. before / after で見る設計指摘の具体例 ここまで読めば、設計指摘の意味やその根底にある考えが分かってきたと思います。 4 節ではサンプルコードを用いて、実際にどんなコードがどんな理由で指摘され、どう直すべきなのかを before / after の形で見ていきます。 4.1. SRP: 「責務が多い」と言われたケース まずは SRP(単一責任原則) の例です。 当時の私は以下のように、1つのクラスで色々な処理をしようとしていました。 before: 1つのクラスに複数の責務がある public class UserService { public void register(User user) { validate(user); save(user); sendNotification(user); } private void validate(User user) { // 入力チェック } private void save(User user) { // データベースに保存 } private void sendNotification(User user) { // 通知メール送信 } } このコードの問題点は、「ユーザー登録」というユースケースの中に、複数の責務が押し込まれていることです。 以下のような別々の変更理由によって、コードの同じ箇所が変更される可能性があります。 入力チェックの仕様が変わる データベースへの保存方法が変わる 通知手段が増える / 変わる after: 責務ごとに分離する public class UserService { private final UserValidator validator; private final UserRepository repository; private final NotificationService notificationService; public void register(User user) { validator.validate(user); repository.save(user); notificationService.notify(user); } } 処理の流れ自体は変わっていませんが、 入力チェック データベースへの保存 通知 が、それぞれ独立した責務として切り出されています。 この変更によって、 仕様変更時の影響範囲が限定される テストしやすくなる 実装意図が読み取りやすくなる といった効果があります。 「責務が多い」という SRP に基づく指摘は、 将来の変更理由が1箇所に集まりすぎている 、すなわち 一人で色々やりすぎ という意味であると理解できます。 4.2. OCP: 「将来増えそう」と言われたケース 次は OCP(開放閉鎖原則)の例です。当時は以下のように、分岐を増やして処理を追加していました。 before: 条件分岐で処理を切り替えている public class PriceCalculator { public int calculate(Order order) { if (order.getType() == OrderType.NORMAL) { return order.getBasePrice(); } else if (order.getType() == OrderType.DISCOUNT) { return order.getBasePrice() * 9 / 10 ; } else if (order.getType() == OrderType.SPECIAL) { return order.getBasePrice() * 8 / 10 ; } throw new IllegalArgumentException(); } } このようなコードは「将来 if が増えそうですね」などと指摘を受けるでしょう。 そして当時の私なら「今はこれで動くし十分では?」と思ったに違いありません。 このコードの問題点は、 注文種別が増えるたびに if が増える 修正のたびにこのクラスを触る必要がある 変更漏れのリスクが高くなる 「今動くかどうか」ではなく「将来どうなり得るか」が問題です。 そもそも設計指摘とはそういうものでした。 after: 振る舞いを分離する public interface PriceStrategy { int calculate(Order order); } public class NormalPriceStrategy implements PriceStrategy { public int calculate(Order order) { return order.getBasePrice(); } } public class DiscountPriceStrategy implements PriceStrategy { public int calculate(Order order) { return order.getBasePrice() * 9 / 10 ; } } public class PriceCalculator { private final PriceStrategy strategy; public int calculate(Order order) { return strategy.calculate(order); } } この形にすると、 新しい価格計算ルールを追加しても既存コードをほぼ触らない 条件分岐が増えない 振る舞いごとに責務が分かれる という状態になります。 「将来増えそう」という OCP に基づく指摘は、将来変更が入る時にコードがどう修正されるかを見ていたのだと分かりました。 変更する時に既存のコードは変更せず、新たにコードを追加するだけで対応できるようにするべきである という OCP の意味が理解できました。 4.3. DRY: 「共通化できそう」と言われたケース 次は DRY の例です。当時は以下のように、コードの複数箇所に同じことを書いていました。 なお DRY には "知識" と "処理" に注目する2パターンがあるので、それぞれ紹介します。 before: 同じコードが複数箇所にある public class OrderService { public int calculateTotal(Order order) { int subtotal = order.getSubtotal(); int tax = subtotal * 10 / 100 ; return subtotal + tax; } public int calculateRefund(Order order) { int subtotal = order.getSubtotal(); int tax = subtotal * 10 / 100 ; return subtotal - tax; } } このコードの問題点を "知識" に注目して挙げると、 税率10%という "知識" が二箇所に書かれている 税率変更時に両方修正が必要 これにより修正漏れのリスクが高くなっています。 after 其ノ壱: 知識を一箇所に集約 public class TaxCalculator { private static final int TAX_RATE = 10 ; public static int calculate( int amount) { return amount * TAX_RATE / 100 ; } } public class OrderService { public int calculateTotal(Order order) { int subtotal = order.getSubtotal(); return subtotal + TaxCalculator.calculate(subtotal); } public int calculateRefund(Order order) { int subtotal = order.getSubtotal(); return subtotal - TaxCalculator.calculate(subtotal); } } こうすれば、例えば税率を8%に変える時、 TaxCalculator の冒頭一箇所のみを修正すれば済みます。 元のコードに比べて修正漏れのリスクが低くなっています。 一方で元のコードの問題点を "処理" に注目して挙げると、 税金計算ロジックが二箇所に書かれている 変更時に両方修正が必要 これらを解決するには、"知識" の場合にならって "処理" を一箇所にまとめれば良いです。 after 其ノ弐: 処理を一箇所に集約 public class OrderService { public int calculateTotal(Order order) { int tax = calculateTax(order.getSubtotal()); return order.getSubtotal() + tax; } public int calculateRefund(Order order) { int tax = calculateTax(order.getSubtotal()); return order.getSubtotal() - tax; } private int calculateTax( int subtotal) { return subtotal * 10 / 100 ; } } 重複していた税計算処理が一箇所のみになるので、この場合も元のコードに比べて修正漏れのリスクが低くなります。 DRY 原則を守るとは、 同じコードをまとめ、修正箇所を減らす ことだと分かりました。 4.4. before / after から学んだこと これらの例から、設計指摘を受けるコードの特徴や改善方法がイメージできたと思います。 またコードの問題点を before / after を通して見ることで、設計が、2 節で述べた「中長期的な保守性」に大きな影響を与えることも分かったと思います。 このような経験から、私は設計指摘が出てしまう原因には実装者とレビュワーの視点の違いがあると考えています。 実装者としては、 仕様を満たしているか テストが通っているか に目が行きがちです。 実際、before のコードはどれも「今の要件」だけを見れば問題ありません。 一方レビュワーは、 機能追加や仕様変更 別の人が触る未来 を考えています。設計指摘はこれらを事前に想像したうえで出されていました。 この視点の違いこそ、レビューで指摘が出る理由です。 それゆえ実装時には 1.2 節で述べたような観点を持つことが重要なのだと思います。 5. 実装時に持つべき視点 設計指摘の意味が分かると、以前とは異なり、実装時点でレビュー視点を持てるようになりました。 実装中に自然と次のような問いを立てるようになりました。 このクラスは、何のために存在しているのか 変更理由は1つに収まっているか 将来どんな仕様変更が入りそうか そのときどこを直すことになりそうか これらは全てこれまでレビューで指摘されてきたポイントであり、別の言い方をすれば、設計原則が言おうとしていることでした。 その結果、レビューで指摘されそうな点に早い段階で気づけるようになりました。 こうして実装時に色々な観点を持てるようになりました。 自分の実装の良し悪しを常に同じ判断基準で考えることができるので、実装のたびにブレることが少なくなったと思います。 もしレビューで設計指摘を受けたときは、これらの観点で一度コードを見直してみてください。 「今動くか」ではなく「将来どう変わるか」を考えるだけで、見えるものが大きく変わると思います。 また、最近から私はレビューする側にも回っています。 レビューされる側として多くの指摘を受けてきた経験は、レビューをする時でも大いに役立っていると感じます。 自分なりに着眼点を持ち、「今の実装が動くかどうか」だけでなく、将来のことを考えてレビューをしています。 6. 同じ立場の人たちへ 実装時あるいはレビュー時に、本記事で挙げたような観点を持っているのといないのでは差があります。 おさらいすると、 クラスや関数の責務は1つか 将来どんな変更が入りそうか そのときどこを直すことになりそうか 修正する箇所は少ないか 他の人が読んだときに意図が伝わるか などであり、重要なのは、これらはどれも「今」ではなく「将来」を見ているということです。 常に同じ判断基準を持ち、経験とともにそれを磨き、増やしていけば、実装やレビューの質は確実に上がると思っています。 7. まとめ この記事では、 なぜレビューやコード設計が重要なのか 抽象的に思える設計指摘の意味や直し方 を、私自身の変化を軸に整理してきました。 コード設計が中長期的な保守性に大きな影響を与えるので、 将来の変更に耐えられるか という視点から設計指摘がなされるのでした。 そして具体的なコードを見て設計への理解をより深め、実装やレビューの時に持つと良い視点としてまとめることができました。 レビューで設計指摘を受けている人やこれからレビューを始める人にはぜひ意識してもらいたいです。 この記事がそのような人の助けになれば嬉しいです。

動画

書籍