功能在开发手机上能跑,代码也提交了,是不是就算完成?

还不算。真正的交付至少要回答四个问题:需求是否做对、异常场景是否可控、别人能否验证、上线出问题能否发现和处理。

核心结论:Flutter 开发交付不是“编码完成”,而是从需求确认到线上观察的闭环。流程可以按风险调整,但可验收、可测试、可追踪和可回退不能缺失。

一套最小交付流程

这不是为了增加审批,而是尽量在成本更低的阶段发现问题。需求阶段发现歧义,通常比上线后返工便宜得多。

第一步:先把需求变成可验证的结果

“增加搜索功能”还不能直接开始编码。至少要确认:

  • 搜索范围和匹配规则;
  • 空关键词、无结果和接口失败如何显示;
  • 是否需要防抖、取消旧请求或保留历史记录;
  • Android、iOS 是否一致;
  • 什么结果算验收通过。

涉及接口、埋点、设计稿、权限或原生能力时,也要确认依赖方和交付时间。

一个简单原则是:如果开发无法写出验收步骤,需求通常还没有真正说清楚。

第二步:编码前先看风险

不是每个任务都需要设计文档,但要知道改动可能影响哪里。

Flutter 项目中经常被忽略的风险包括:

  • 状态应该属于页面、模块还是全局;
  • 异步返回后页面是否仍然 mounted
  • 连续请求是否会出现旧数据覆盖新数据;
  • Controller、订阅、Timer 是否有明确的释放方;
  • 插件是否同时支持目标 Android、iOS 和当前 Flutter 版本;
  • 后端接口是否兼容仍未升级的旧版 App;
  • 功能关闭、接口降级或版本回退时会发生什么。

高风险改动要提前设计开关、降级和监控。低风险文案调整可以简化流程,但不能跳过基本验证。

第三步:让代码保持可检查

编码过程不只是把功能拼出来,还要让后续修改者看得懂。

日常可以守住几条底线:

  • 一个改动围绕一个明确目标,避免混入无关重构;
  • 业务状态有清晰来源,避免多个对象同时修改同一事实;
  • Loading、空数据、错误和成功状态都能表达;
  • 异步代码处理异常、生命周期和竞态;
  • 新增依赖说明用途,并提交对应锁文件和原生配置;
  • 生成代码按项目约定重新生成,不手工修改生成产物。

格式化、静态检查和测试命令应以项目脚本或 CI 配置为准。常见基础检查包括:

dart format --output=none --set-exit-if-changed .
flutter analyze
flutter test

这些命令能挡住一部分问题,但不能代替真实设备上的业务验证。

第四步:自测不能只走成功路径

开发自测至少要覆盖:

  1. 正常流程是否符合验收条件;
  2. 空数据、错误、超时和重复点击;
  3. 返回、切后台、旋转或页面销毁后的行为;
  4. 弱网、断网和请求重试;
  5. Android 与 iOS 的关键差异;
  6. 窄屏、长文本、大字体和键盘弹出;
  7. 权限拒绝、永久拒绝和重新授权。

涉及性能时,要在目标设备的 Profile 或 Release 模式下测量。Debug 模式卡顿不能直接当成线上结论。

自测完成后,保留必要证据:录屏、截图、测试账号、构建号、日志时间范围或性能 Trace。这样测试和评审者不用重新猜测你验证了什么。

第五步:提交一份别人能评审的变更

一个可评审的 Pull Request 应该说明:

  • 为什么要改;
  • 主要改了什么;
  • 哪些文件或模块风险较高;
  • 如何验证;
  • 是否有界面截图或录屏;
  • 是否修改接口、存储、权限、插件或原生配置;
  • 上线后如何观察,出问题如何关闭或回退。

评审不是检查代码风格这么简单。它还要确认状态边界、生命周期、错误处理、平台差异和测试范围是否合理。

CI 通过也不代表功能正确。CI 证明的是自动检查范围内没有发现问题,业务验收仍需要人或端到端测试完成。

第六步:发布前确认“这一包是谁”

发布产物必须能对应到源码和配置。至少保留:

  • App 版本号和构建号;
  • 提交或流水线编号;
  • Flavor、后端环境和功能开关;
  • Android 与 iOS 构建结果;
  • 与当前构建匹配的符号文件;
  • 发布说明、影响范围和回归结果。

签名、权限、推送、深链、网络安全配置和插件原生配置,往往只有 Release 构建或真机环境才能暴露问题。

移动端也不能假设“发错了马上回滚”。商店审核、分批发布和用户不升级都会让多个版本长期共存,因此后端兼容和功能开关非常重要。

第七步:上线不是流程终点

发布后要观察与本次改动直接相关的指标,例如:

  • Crash 和非致命异常;
  • 接口错误率与耗时;
  • 页面成功率或关键业务转化;
  • 启动、卡顿和内存指标;
  • 用户反馈是否集中在特定平台或版本。

先小范围灰度,再根据指标扩大范围。发现异常时,要能快速判断是停止灰度、关闭功能、服务端降级还是发布修复版本。

一份可直接使用的完成清单

- [ ] 验收条件已确认,没有未决歧义
- [ ] 正常、空、错、加载状态已处理
- [ ] 异步生命周期、竞态和资源释放已检查
- [ ] Android / iOS 关键流程已验证
- [ ] 格式化、静态检查和相关测试通过
- [ ] PR 包含影响范围、验证证据和风险说明
- [ ] Release 构建、版本和符号文件可追踪
- [ ] 灰度、监控、开关或回退方案已确认

清单不是越长越专业。团队应该根据事故和遗漏持续调整,把真正高频的风险留下来。

最后记住这几点

  • 代码完成只是交付流程的中间节点。
  • 没有验收条件,就无法判断是否做对。
  • 自测要覆盖异常、生命周期、平台差异和真实设备。
  • CI、评审和业务验收解决的是不同问题,不能互相替代。
  • 发布产物必须可追踪,线上变化必须可观察。
  • 移动端回退不即时,兼容、灰度和功能开关要提前设计。

问答复盘

Q1:功能在开发手机上正常,为什么还不能交付?

答: 单一设备只验证了一个环境。还要确认验收条件、异常路径、平台差异、构建配置和上线风险。

Q2:小改动也必须走完整流程吗?

答: 流程可以按风险缩减,但基本检查和可验证证据不能省。文案修改与支付链路改造不应使用相同强度。

Q3:CI 全绿是否代表可以直接上线?

答: 不代表。CI 只能覆盖已经配置的自动检查,无法自动证明业务逻辑、真机行为和用户体验全部正确。

Q4:Flutter 自测为什么要关注 Android 与 iOS?

答: 插件、权限、生命周期、键盘、返回行为和系统限制可能不同,共享 Dart 代码不代表平台行为完全一致。

Q5:性能问题为什么要在 Profile 或 Release 模式验证?

答: Debug 模式包含额外调试开销,不能代表发布产物。还要在目标设备和对应刷新率下测量。

Q6:移动端发布为什么特别强调兼容和功能开关?

答: 用户不会同时升级,应用商店回退也不即时。线上通常长期存在多个客户端版本,需要后端兼容和可控降级。