name: 功能开关开发者 description: 指导系统地从代码库中移除功能开关,并进行自动清理检测。用于移除功能标志、永久启用开关或移除开关后清理未使用的代码。
功能开关开发者
状态: 活跃
自动激活于: 功能标志/开关移除,重构开关后的清理
相关技能: 代码生成开发者, ios开发指南, 代码审查开发者
目的
指导系统地从代码库中移除功能开关(功能标志),并进行自动清理检测。确保在移除开关后没有孤儿代码、未使用的组件或遗留文件。
当此技能激活时
- 用户提及: “移除开关”, “删除功能标志”, “永久启用功能开关”
- 用户编辑:
FeatureDescription+Flags.swift - 完成开关移除后: 帮助识别清理机会
功能开关移除工作流程
阶段1: 移除前分析
在移除任何开关之前,收集完整信息:
-
找到开关定义:
# 位置: Modules/AnytypeCore/AnytypeCore/Utils/FeatureFlags/FeatureDescription+Flags.swift rg "static let toggleName" --type swift -
检查defaultValue 以确定保留哪个分支:
defaultValue: true→ 保留TRUE分支,移除FALSE分支defaultValue: false→ 保留FALSE分支,移除TRUE分支
-
搜索所有使用情况:
rg "toggleName" --type swift -
识别使用模式:
- 直接:
if FeatureFlags.toggleName { ... } - 反转:
if !FeatureFlags.toggleName { ... }或guard !FeatureFlags.toggleName - 复合:
if FeatureFlags.toggleName && otherCondition { ... } - 赋值:
let value = FeatureFlags.toggleName ? a : b - 状态:
@State private var toggle = FeatureFlags.toggleName
- 直接:
-
列出受影响文件 并供用户审查
阶段2: 开关移除
系统移除过程:
-
移除条件检查并简化:
示例1 - 简单条件 (defaultValue: true):
// 之前 if FeatureFlags.toggleName { // 功能代码 } // 之后 (保留true分支) // 功能代码示例2 - 三元运算符 (defaultValue: true):
// 之前 VStack(spacing: FeatureFlags.toggleName ? 8 : 0) // 之后 VStack(spacing: 8)示例3 - 反转逻辑 (defaultValue: true):
// 之前 guard !FeatureFlags.toggleName else { return } oldCode() // 之后 (标志为true,所以guard失败,移除整个块) // [整个块删除]示例4 - 状态变量 (defaultValue: true):
// 之前 @State private var toggle = FeatureFlags.toggleName if toggle { newUI() } else { oldUI() } // 之后 newUI() // 注意: @State变量在清理阶段移除 -
移除功能标志定义:
// 从以下删除: Modules/AnytypeCore/AnytypeCore/Utils/FeatureFlags/FeatureDescription+Flags.swift static let toggleName = FeatureDescription(...) -
运行代码生成:
make generate这会自动更新
FeatureFlags+Flags.swift。 -
验证移除:
rg "toggleName" --type swift # 应该返回无结果
阶段3: 自动清理检测 ⭐
关键: 移除开关后,系统检查孤儿代码:
3.1 未使用的状态变量
搜索仅用于开关的 @State 变量:
# 查找模式如: @State private var someToggle = FeatureFlags.toggleName
rg "@State.*=.*FeatureFlags" --type swift
操作: 如果不再使用,移除整个 @State 变量声明。
3.2 未使用的视图组件
当开关控制显示哪个UI组件时,一个组件可能现在未使用:
检测模式:
- 开关在ComponentA和ComponentB之间切换
- 移除后,只使用一个
- 在整个代码库中搜索未使用组件的名称
vaultBackToRoots示例:
// 之前
if !vaultBackToRootsToggle {
SpaceCardLabel(...) // 这个变得未使用
} else {
NewSpaceCardLabel(...)
}
// 之后
NewSpaceCardLabel(...)
// 清理: SpaceCardLabel 现在未使用
rg "SpaceCardLabel" --type swift # 检查是否在其他地方使用
# 如果仅在其自己的文件中找到 → 删除文件
操作:
- 搜索未使用组件名称:
rg "UnusedComponentName" --type swift - 如果仅在定义文件和注释中找到 → 删除文件
- 更新注释中的引用
3.3 未使用的ViewModels / 服务类
开关移除可能留下整个类未使用:
检测:
# 对于每个有条件使用的主要组件:
rg "UnusedViewModel" --type swift
rg "class UnusedViewModel" --type swift
操作: 删除未使用的ViewModels、它们的文件和DI注册。
3.4 未使用的导入
简化后,import AnytypeCore 可能仅用于 FeatureFlags:
检测:
- 文件导入
AnytypeCore - 仅使用
FeatureFlags.toggleName - 移除后,没有其他
AnytypeCore使用
操作: 移除未使用的导入。
3.5 孤儿参数 / 属性
开关门控功能可能有不再需要的参数:
示例:
// 之前
func configure(showFeature: Bool) {
if showFeature && FeatureFlags.toggle { ... }
}
// 开关移除后
func configure(showFeature: Bool) {
if showFeature { ... }
}
// 潜在清理: showFeature 是否仍然需要?
操作: 审查函数签名并移除不必要的参数。
3.6 测试清理
开关移除影响测试:
检查:
- 具有开关相关属性的模拟对象
- 专门针对开关行为的测试用例
- 带有开关配置的测试设置代码
要检查的文件:
rg "toggleName" AnyTypeTests/ --type swift
rg "toggleName" "Anytype/Sources/PreviewMocks/" --type swift
操作: 更新或移除已删除代码路径的测试。
阶段4: 最终验证
提交前:
-
Grep验证:
rg "toggleName" --type swift # 应该为空 -
编译检查:
- 提醒用户在Xcode中验证编译
- Claude由于缓存无法验证此点
-
生成更新的提交消息:
IOS-XXXX 移除了 [toggleName] 开关 -
审查清理摘要:
- 列出所有修改的文件
- 列出所有删除的文件
- 注意任何剩余的手动检查需要
清理检查清单模板
每次开关移除后使用此检查清单:
## [toggleName] 的清理验证
- [ ] 从FeatureDescription+Flags.swift中移除开关定义
- [ ] `make generate` 成功运行
- [ ] 所有条件使用移除
- [ ] 没有grep结果针对开关名称
- [ ] 未使用的@State变量移除
- [ ] 未使用的视图组件识别并删除
- [ ] 未使用的ViewModels/服务删除
- [ ] 未使用的导入移除(尤其是AnytypeCore)
- [ ] 孤儿函数参数移除
- [ ] 测试更新(检查AnyTypeTests/和PreviewMocks/)
- [ ] 引用旧组件的评论更新
- [ ] Xcode编译验证(由用户完成)
常见模式与陷阱
反转逻辑
注意 !FeatureFlags.toggle:
// 如果defaultValue: true
if !FeatureFlags.toggle {
oldCode() // 这个分支从不运行,删除它
}
复合条件
正确简化 条件:
// 之前 (defaultValue: true)
if FeatureFlags.toggle && userHasPermission {
showFeature()
}
// 之后
if userHasPermission {
showFeature()
}
Guard语句
小心 guards:
// 之前 (defaultValue: true)
guard !FeatureFlags.toggle else { return }
performOldBehavior()
// 之后 (开关为true,guard返回,整个块是死代码)
// 删除整个块
与其他技能的集成
- 代码生成开发者: 引用
make generate命令和故障排除 - ios开发指南: Swift重构模式,导入管理
- 代码审查开发者: 清理标准,确保PR中没有孤儿代码