《游戏服务端架构·平台化与架构治理》9.2 平台团队、产品团队与 SRE

《群岛纪元》大促前,SRE 被拉进产品群直接改配置,平台团队则按工单接需求,三类团队一起退化成外包与运维。本节给出产品、平台、SRE 的决策边界与交付接口,区分服务式、协作式、赋能式三种交互模式及其退出条件,并给出职责冲突检测、契约覆盖检查与可运行的 Go 夹具。

大促前,三类团队一起变形

《群岛纪元》赛季更新前一周,产品团队要在活动页加一条充值返利规则,平台团队的排期已经排到两周后,SRE 被拉进临时群,直接改了网关上的限流配置。事故发生后,三个人都能说“我只是配合”,但没有人能说清是谁批准了这次变更。

这类变形不是个别人的问题,而是团队接口缺失后的必然结果:产品团队要速度,平台团队要稳定,SRE 要可靠性,三者的目标没有在接口上对齐,就只能靠临时拉群解决。拉群的成本很低,所以它会反复发生,直到某次变更把生产打挂。

三类团队的分工,不是谁技术更强,而是各自回答不同的问题、拥有不同的决策权。边界清楚时,合作可以很频繁;边界不清时,合作越频繁越危险。

三类团队分别回答一个问题

团队核心问题主要交付典型越界
产品团队玩家要什么,这个功能值不值得做玩法、业务服务、活动逻辑顺手维护一套通用平台
平台团队多个产品如何共用一种能力版本化能力、自助工具、容量按工单替产品写业务
SRE系统在多坏的情况下还能撑住SLO、演练、应急与可观测性接管应用变更与发布审批

产品团队拥有业务决策权,因为它离玩家最近,也承担收入结果。平台团队拥有能力接口的决策权,因为它要对多个消费者负责,不能为单个产品的排期破坏兼容性。SRE 拥有可靠性标准的决策权,因为它要在故障发生时判断先保什么、后保什么。

三种决策权会冲突,冲突本身是健康的;不健康的是没有裁决规则。一个可用的规则是:业务范围听产品,接口与容量听平台,可靠性目标由产品提出、SRE 核算成本后共同确认,事故现场由 SRE 指挥、产品决定业务取舍。规则不需要覆盖所有情况,但必须覆盖最常吵的三件事。

平台以契约交付,不以工单交付

平台团队最容易退化成外包,标志不是“接需求”,而是“只接定制需求”。一旦所有消费者都走 bespoke 通道,平台就没有可复用的能力,只有一张越来越长的排期表。

平台交付分三档,成本与可扩展性完全不同:

交付档位形态适用条件主要成本
自助式控制台、API、模板,产品自己接入需求同质、量大前期建设重,后期边际成本低
协助式平台提供方案,产品执行接入需求有差异但可复用需要解决方案工程师
定制式平台替产品实现探索期、一次性活动不可扩展,必须设退出时间

定制式不是原罪,但必须有配额和退出时间。示例做法是每个季度定制需求不超过平台人力的两成,且每一单都写明“何时迁回自助式”。没有退出时间的定制,就是外包。

团队接口的最小集比完整契约更窄:版本、SLO、支持窗口、废弃策略、升级路径。第 1 章已经展开契约六要素,这里只强调团队视角——这五项决定了产品团队能否在不找人的情况下判断“能不能用、什么时候不能用、坏了找谁”。

平台的成功指标也应该换。工单关闭率会鼓励平台快速关单,而不是鼓励产品自助;更有用的指标是自助接入比例、版本迁移完成率、从申请到可用的端到端时长。指标换了,团队行为才会换。

SRE 的参与方式:SLO、演练、应急

SRE 的产品是可靠性,不是“帮忙看下”。它的交付物包括:可执行的 SLO 与错误预算、故障演练、应急指挥机制、可观测性工具链。这四样都不要求 SRE 拥有业务服务的代码。

事故现场的分工可以写得很具体:

角色由谁承担决定什么不决定什么
事故指挥SRE定级、分工、对外沟通节奏具体业务取舍
业务决策产品团队关哪个渠道、是否停活动技术止血方案
能力修复平台团队网关、存储、消息等能力恢复业务功能是否上线
服务修复服务 owner应用层代码与配置回滚跨产品资源调度

SRE 一旦开始日常改业务配置、审批每一次发布,它就已经变成运维团队,只是名字没换。判断标准很直接:过去一个月,SRE 有多少时间花在业务变更上?如果超过三分之一,可靠性工作一定被挤掉。

产品团队保留业务决策权

平台团队可以拒绝一个不兼容的接口需求,但不能判断“这个玩法不值得做”;SRE 可以因为错误预算耗尽而建议冻结发布,但不能决定“这个活动必须上线”。决策权错位的典型症状是:平台或 SRE 成了产品排期的实际审批人,产品团队则把“说服平台”当成主要工作。

决策最终负责人必须咨询可否否决
功能范围与优先级产品团队平台、SRE平台可因兼容性否决接口方案
平台接口与版本平台团队产品消费者产品可拒绝升级,但不能阻止废弃
SLO 目标产品与 SRE 共同平台容量SRE 可指出目标不可达
事故指挥SRE服务 owner产品不能绕过指挥链
业务变更回滚产品团队SRESRE 可因可靠性风险要求先止血
容量配额平台与财务产品产品不能在配额外自行扩容

升级路径要限层级、限时间。一个问题最多升级一次,且在约定时间内给结论;长期双签等于没有责任人。升级不是坏事,长期停在升级状态才是。

三种交互模式与时限

本节的协作周数、工时比例、定制配额与检查阈值均为示例值,应按团队规模与业务节奏校准。

团队之间不是只有“合作”一种关系。把交互模式显式化,可以避免协作变成事实上的合并:

模式含义适用场景时限与退出
服务式一方按契约提供能力,另一方自助消费稳定能力、多消费者长期,但接口有版本
协作式双方共同交付一个结果迁移、新能力共建不超过 12 周,必须有退出计划
赋能式一方把能力传给另一方,之后退出新团队接手、演练带教有明确目标与结束周

协作式最容易失控。它开始时理由充分——“这次迁移特殊,需要一起做”——但如果没有退出计划,六个月后双方仍在同一个群里做日常决策,组织上已经是合并,架构上却没有对应的边界。规则应该硬一点:协作超过 12 周,要么转为服务式,要么正式合并团队,不允许无限期灰色状态。

平台团队到产品团队做协作,还有一层特殊风险:平台开始替产品做业务实现,短期看是帮忙,长期看平台被排期拖住,产品也没有建立起自己的能力。这类协作应该改成赋能式:平台出方案和工具,产品团队执行,平台在结束周退出。

四个反模式

反模式典型症状领先指标对策
平台变外包需求都走定制,排期越来越长定制需求占比超过一半建自助能力,定制设配额与退出时间
SRE 变运维SRE 改业务配置、审批发布SRE 三分之一时间在业务变更收回应用变更权,只保留可靠性职责
产品各自造平台两个产品各有一套匹配或支付同名能力有多个 owner能力审计,重复能力合并
协作永久化双方长期共同决策,无退出协作超过 12 周仍无退出计划转服务式或正式合并

四个反模式有一个共同点:它们在短期内都提高了交付速度,所以很难被叫停。识别它们不能靠季度复盘时的感觉,要靠领先指标——定制占比、SRE 业务工时、同名能力数量、协作周数。指标一旦越线,就应该触发评审,而不是等到事故。

职责冲突检测

internal/teamtopology 把这些边界写成可执行规则。CapabilityAudit 按能力类型检查 owner 是否匹配:玩法能力归产品,共享平台能力归平台,可靠性能力归 SRE;同名能力出现多个 owner 时直接报冲突。同包的 InteractionAudit 检查协作是否超过 12 周、是否有退出计划,ContractGaps 检查每一个平台或 SRE 的服务式交互是否有契约覆盖。

// file: internal/teamtopology/capability.go
// tier: L1
// verify: go test ./internal/teamtopology/
package teamtopology

import (
	"fmt"
	"sort"
)

// TeamType is the role a team plays in the delivery model, not its seniority.
type TeamType string

const (
	Product  TeamType = "product"
	Platform TeamType = "platform"
	SRE      TeamType = "sre"
)

// Team is the minimum topology record. Capabilities are declared separately so
// ownership can be audited without reading a wiki page.
type Team struct {
	Name string
	Type TeamType
}

// CapabilityKind decides which team type may own a capability.
type CapabilityKind string

const (
	GameFeature    CapabilityKind = "game-feature"
	SharedPlatform CapabilityKind = "shared-platform"
	Reliability    CapabilityKind = "reliability"
)

// Capability is one reusable or game-specific ability with a single owner.
type Capability struct {
	Name  string
	Kind  CapabilityKind
	Owner string
}

// Conflict is one topology violation. Subject is a team, capability, or
// "from->to" pair so the report can be diffed across quarters.
type Conflict struct {
	Subject string
	Rule    string
	Detail  string
}

func teamIndex(teams []Team) (map[string]Team, []Conflict) {
	idx := make(map[string]Team, len(teams))
	var out []Conflict
	for _, t := range teams {
		if t.Name == "" {
			out = append(out, Conflict{Subject: "<team>", Rule: "team-without-name",
				Detail: "team name is empty"})
			continue
		}
		if _, exists := idx[t.Name]; exists {
			out = append(out, Conflict{Subject: t.Name, Rule: "duplicate-team",
				Detail: "team declared more than once"})
			continue
		}
		idx[t.Name] = t
	}
	return idx, out
}

func sortConflicts(cs []Conflict) {
	sort.SliceStable(cs, func(i, j int) bool {
		if cs[i].Subject != cs[j].Subject {
			return cs[i].Subject < cs[j].Subject
		}
		if cs[i].Rule != cs[j].Rule {
			return cs[i].Rule < cs[j].Rule
		}
		return cs[i].Detail < cs[j].Detail
	})
}

var expectedOwner = map[CapabilityKind]TeamType{
	GameFeature:    Product,
	SharedPlatform: Platform,
	Reliability:    SRE,
}

func ownerRule(kind CapabilityKind, ownerType TeamType) (string, bool) {
	switch {
	case kind == GameFeature && ownerType == Platform:
		return "platform-owns-game-feature", true
	case kind == GameFeature && ownerType == SRE:
		return "sre-owns-game-feature", true
	case kind == SharedPlatform && ownerType == Product:
		return "product-rebuilds-platform", true
	case kind == SharedPlatform && ownerType == SRE:
		return "sre-owns-platform-capability", true
	case kind == Reliability && ownerType == Product:
		return "product-owns-reliability", true
	case kind == Reliability && ownerType == Platform:
		return "platform-owns-reliability", true
	}
	return "", false
}

// CapabilityAudit checks three things: every capability has one known owner,
// the owner's team type matches the capability class, and no two teams claim
// the same capability name. The last rule catches "each product builds its own
// matchmaking" before it becomes three platforms.
func CapabilityAudit(teams []Team, caps []Capability) []Conflict {
	idx, out := teamIndex(teams)
	seen := make(map[string]bool, len(caps))
	for _, c := range caps {
		subject := c.Name
		if subject == "" {
			subject = "<capability>"
		}
		if seen[c.Name] && c.Name != "" {
			out = append(out, Conflict{Subject: subject, Rule: "duplicate-capability",
				Detail: "capability declared by more than one team"})
			continue
		}
		seen[c.Name] = true

		want, knownKind := expectedOwner[c.Kind]
		if !knownKind {
			out = append(out, Conflict{Subject: subject, Rule: "unknown-capability-kind",
				Detail: fmt.Sprintf("kind %q is not recognized", c.Kind)})
			continue
		}
		owner, ok := idx[c.Owner]
		if c.Owner == "" {
			out = append(out, Conflict{Subject: subject, Rule: "capability-without-owner",
				Detail: "capability has no owning team"})
			continue
		}
		if !ok {
			out = append(out, Conflict{Subject: subject, Rule: "unknown-capability-owner",
				Detail: fmt.Sprintf("owner %q is not a declared team", c.Owner)})
			continue
		}
		if owner.Type == want {
			continue
		}
		if rule, bad := ownerRule(c.Kind, owner.Type); bad {
			out = append(out, Conflict{Subject: subject, Rule: rule,
				Detail: fmt.Sprintf("%s capability owned by %s team", c.Kind, owner.Type)})
		}
	}
	sortConflicts(out)
	return out
}

本机验证命令与结果:

cd .local/checks/gsarch-lab
go test -count=1 ./internal/teamtopology/
ok  	gsarchlab/internal/teamtopology	0.254s

该包共有 4 个表驱动测试,覆盖能力归属、交互时限、契约缺口与输出确定性(重复声明在能力归属用例内);gofmt -l 无输出。

三个失败路径

平台 KPI 是工单关闭率。 平台会优先处理容易关单的定制需求,自助能力没人建设。修复不是换一个数字,而是把指标换成自助接入比例和迁移完成率,并让定制需求显式占用配额。

SRE 被排进业务需求。 短期看是补人力,长期看可靠性工作没有 owner。修复是把 SRE 的排期与业务排期分开,可靠性项目有独立预算;事故支援可以临时发生,但不能成为常态。

契约只写“支持”,不写 SLO。 双方对“支持”的理解不同:平台认为是工作时间响应,产品认为是 7×24 兜底。修复是把支持窗口、响应时间、升级路径写成可验证的条款,并在每季度复核达成率。

检查表

  1. 三类团队是否各自有明确的决策权清单,而不是只有职责描述?
  2. 平台是否有自助式交付,定制需求是否设了配额与退出时间?
  3. SRE 是否仍拥有应用变更权或发布审批权?
  4. 是否存在同名能力由多个团队 owner?
  5. 每一个平台或 SRE 的服务式交互是否都有契约覆盖?
  6. 协作式交互是否都有不超过 12 周的期限和退出计划?
  7. 赋能式交互是否写明了目标能力与结束周?
  8. 事故现场是否有唯一的指挥角色,产品是否保留业务取舍权?
  9. 平台的考核指标是否包含自助接入与迁移完成率?
  10. 过去一个季度,SRE 的业务变更工时是否低于三分之一?

第 3、4、5 条是硬约束:出现“是”或“否”的异常,就应该在季度评审上给出整改时间,而不是继续观察。

小结

三类团队的分工是决策权分工:产品决定做什么,平台决定能力如何被复用,SRE 决定可靠性标准与事故指挥。平台以契约交付,至少写清版本、SLO、支持窗口、废弃策略和升级路径;SRE 以 SLO、演练和应急参与,不接管应用变更;产品保留业务决策权。交互模式要显式化,协作不超过 12 周并有退出计划,赋能要有目标和结束周。平台变外包、SRE 变运维、产品各自造平台、协作永久化,是四个需要用领先指标提前拦住的退化解。

下一篇讨论组织变更如何落到架构:合并、拆分、外包、工作室独立,分别要改服务边界、数据、权限、计费和流水线,过渡期必须有退出时间表。

阅读导航:上一节:9.1 团队边界与服务所有权 · 下一节:9.3 组织变化如何反映到架构 。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「books」更多文章

  1. 《互联网产品的 AI 架构·平台化与治理》附录 C 平台化评审清单
  2. 《互联网产品的 AI 架构·平台化与治理》附录 B 能力目录与配额字段速查
  3. 《互联网产品的 AI 架构·平台化与治理》附录 A 治理角色与职责速查