ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

App开发进度50%后:技术债清理、网络层加固与发布准备

2026/8/30 12:24:47 拓冰建站 浏览量
App开发进度50%后:技术债清理、网络层加固与发布准备 Hermes Studio App 的开发进度停在 50% 时真正棘手的问题往往不是功能还差一半而是已经写出来的那一半里有太多“看起来能用、其实不确定”的代码。很多 App 项目都会在这个节点进入一个缓冲期核心页面能打开演示路径能走通但一旦把它交给测试人员、换一台真机、切到正式接口立刻冒出加密、证书、超时、签名、最低系统版本一类的问题。本文不讨论 Hermes Studio 的具体业务功能而是把它作为典型的 App 开发中段项目梳理从 50% 推进到可发布版本需要做的技术工作包括技术栈与项目结构、网络与数据层、测试与抓包、兼容性与签名、发布前检查清单。这些内容对 Flutter、React Native、uni-app 以及原生 Android/iOS 工程同样适用适合已经进入开发中段的同学对照检查。1. 为什么说 50% 不意味着“快了”1.1 进度过半时代码里通常已经藏了三类技术债第一个阶段的核心目标是把功能跑起来很多决定是为了“先用起来”而不是“长期使用”做的。开发到 50% 时代码里通常已经积累了三种明显的技术债。第一类是需求边界漂移。开发过程中不断加入“顺手做一下”的需求导致核心流程被旁边的分支功能干扰。比如一个登录模块里夹带了分享、埋点、广告位跳转最后登录本身反而没有做好异常分支。第二类是临时解决方案残留。为了给前端联调或给团队演示代码里经常会出现写死的 baseUrl、假的登录态、被注释掉的校验逻辑甚至直接跳过异常分支。这些代码在演示时效果好但在发布前就是隐患。第三类是验证停留在“能跑”层面。只在开发设备上点击过主要路径没有换低端机没有模拟弱网没有处理空数据、超时、接口返回异常。到了 50% 这个节点继续往下加功能只会让问题更集中地爆发。这个阶段最该做的不是继续堆功能而是先对代码库做一次技术债盘点。把临时方案标出来把写死的地址找出来把明显不合理的模块边界调整好。否则后 50% 的迭代会越来越慢因为每次改动都可能触发已经坏掉的部分。1.2 开发目标要从“功能验证”切换到“可交付保障”前半程的问题是“功能能不能做出来”后半程的问题是“功能能不能稳定交付”。这是两种完全不同的开发模式。功能验证阶段允许写一次性代码允许在真机调试时不断重启允许后端接口没准备好时先返回假数据。可交付阶段则要求代码可构建、配置可切换、错误可追踪、版本可回滚、包可分发。每一条都可验证不是一个模糊的感觉。以 Hermes Studio App 为例如果当前还不能做到“随便拉一台真机、安装最新产物、走完注册到使用核心流程、全程看不到明显异常”那说明 50% 只是功能覆盖率的数字并不是可交付状态的进度。因此建议在进入下半程前把团队目标从“完成功能列表”调整为“完成一个可发布版本”。功能列表仍然重要但每一项功能必须有明确的完成标准而不是“页面能打开”。1.3 用一张里程碑检查表确认项目状态在项目进入后半程之前可以先做一次健康检查。下面是一张可以直接拿去开会的检查清单检查项检查重点合格标准主流程端到端跑通注册、登录、核心业务操作、退出在至少一台真机上完整走通多环境配置分离开发、测试、生产环境是否各自独立切换环境不引起数据互相污染网络异常处理超时、断网、HTTP 5xx、空数据弱网下不闪退有用户可理解提示崩溃日志采集是否接入崩溃平台人为制造崩溃后能收到上报自动化测试覆盖核心路径是否有回归保护登录、拉取数据、状态变更测试通过签名与证书本地和 CI 都能生成同构产物CI 构建的安装包可正常安装版本管理包名、版本号、构建号是否规范每次构建有唯一版本标识这张表不需要一次性全部做完但每一项都要有明确负责人和截止时间。50% 这个节点最适合做这件事因为此时改动成本还低于发布前一周。2. 技术栈与项目结构后半程不能继续“凑合”2.1 原生还是跨平台这个阶段不要轻易迁移开发到 50% 再讨论技术选型已经是一个偏晚的话题。除非当前技术栈严重阻塞核心功能否则不要在这个阶段做跨端迁移。迁移意味着重新适配导航、权限、存储、推送、支付等模块工作量和踩坑数量远大于预期。以下对比只用于帮助团队确认“是否真的需要迁移”方案适用场景常见优势常见代价原生 iOS深度依赖系统能力、需要极致性能系统 API 最新、调试链路最短无法覆盖 Android人力成本翻倍原生 Android需要随时适配碎片化设备权限和硬件控制完整需要同时维护 iOS 版本Flutter团队 Dart 水平成熟、UI 定制多跨端一致性好、渲染性能稳定插件缺失时需写原生桥接React Native团队前端经验多、需要热更新生态大、前端复用依赖版本和原生联动容易出问题uni-app主要面向小程序和部分移动端一套代码多端发布深度原生能力受限如果 Hermes Studio App 当前没有遇到无法绕过的性能瓶颈或系统能力缺失不建议在 50% 时换技术栈。更合理的方式是把技术方案中已经证明低效的部分单独抽出做局部替换而不是整体迁移。2.2 按功能模块重排目录而不是按页面堆文件项目进入后半程后最容易影响效率的是到处都是“页面文件夹”共享代码被放到随意位置。推荐按功能模块组织代码让同一业务域的页面、数据、组件放在一起这样既方便定位也方便后续做模块化的按需加载。以 Flutter 工程为例目录可以按下面的方式调整lib/ core/ config/ # 多环境配置 network/ # 网络客户端与拦截器 error/ # 错误码与业务异常 features/ auth/ data/ # 接口、模型、缓存 domain/ # 业务逻辑 presentation/ # 页面与状态管理 workspace/ settings/ shared/ widgets/ # 通用组件 utils/ # 工具函数按功能模块拆分的核心收益是减少跨模块耦合。改登录功能时不需要碰工作台页面增加新页面时也能从已有模块复制完整结构。原生 Android 的 package、iOS 的文件夹分组、uni-app 的页面分包思路都一样。2.3 锁定依赖版本避免“本地能跑、拉下来不能编”很多团队在开发前半程依赖写得很宽泛比如 Flutter 的^2.0.0、npm 的^1.2.0拉下来的人可能装到不同的次版本导致某些 API 行为不一致。此时最优先的工作是把依赖版本锁死。Flutter 看pubspec.lockReact Native / Node 工程看package-lock.jsonAndroid 工程建议使用 Gradle Version Catalog 统一管理依赖版本iOS 工程则要保证Podfile.lock被提交到版本库。锁版本不只是为了可复现更是为了排查问题时能确定“当前代码是在哪一组依赖上写出来的”。如果大家环境里的依赖各不相同很难对比同一个崩溃日志。2.4 环境配置与全局配置分离开发环境、测试环境、生产环境的 baseUrl、App 名称、包名后缀、日志开关、调试开关都应该可以由配置文件区分而不是每次切换都改代码再打包。# app_env.yaml dev: app_name: Hermes Studio Dev base_url: https://dev-api.example.com enable_debug: true enable_log: true test: app_name: Hermes Studio Test base_url: https://test-api.example.com enable_debug: true enable_log: true prod: app_name: Hermes Studio base_url: https://api.example.com enable_debug: false enable_log: false上面只是示例结构实际项目要替换成自己的域名和配置项。重点是把配置当成代码的一部分进行版本管理而不是存在某个开发者的本地文件里。这样进入后半程后任何人拉取代码都能快速跑出可联调的环境。3. 网络、认证与数据最该提前铺好的底层3.1 接口层统一封装而不是到处写请求50% 之后最容易被后续改动波及的是网络层。如果代码里每个页面都直接创建网络请求没有统一入口接口字段一旦变更需要改的地方会非常多。推荐做法是建一个统一网络客户端把基础 URL、超时时间、公共 header、拦截器都收敛到一处。下面是基于 Flutter Dio 的最小示意import package:dio/dio.dart; class ApiClient { ApiClient({ required this.baseUrl, required this.tokenProvider, }) : _dio Dio( BaseOptions( baseUrl: baseUrl, connectTimeout: const Duration(seconds: 10), receiveTimeout: const Duration(seconds: 10), ), ) { _dio.interceptors.add( InterceptorsWrapper( onRequest: (options, handler) { final token tokenProvider(); if (token ! null token.isNotEmpty) { options.headers[Authorization] Bearer $token; } handler.next(options); }, onError: (error, handler) async { if (error.response?.statusCode 401) { final refreshed await refreshToken(); if (refreshed) { handler.resolve(await _dio.fetch(error.requestOptions)); return; } } handler.next(error); }, ), ); } final Dio _dio; final String baseUrl; final String Function() tokenProvider; }这段代码只是示意实际项目还要处理并发刷新、请求取消、超时重试、错误码映射等。核心思想是让业务页面不感知 token 存放和刷新细节页面只调用业务接口。3.2 Token 过期与 401 统一处理登录态过期是 App 运行过程中必然发生的情况不能只在每个页面单独 catch。统一在拦截器里处理 401可以让返回登录页、刷新 token、重放请求这些逻辑集中。要注意一个细节多个请求同时收到 401 时不要每个都触发一次 token 刷新否则后端会被刷爆。正确做法是把正在刷新 token 的请求保存起来等刷新完成后再统一重放。这种并发控制逻辑虽然不复杂但放到后期再补会非常麻烦。3.3 弱网、缓存和幂等设计后半程需要明确网络请求在弱网下的行为。超时时间要按接口类型区分普通查询可以短一些上传和下载需要更长。请求失败后是否自动重试要看接口是否幂等查询类接口可以重试创建订单、转账、提交表单这类写操作不能盲目重试。App 端的缓存策略也要提前定清楚。列表数据可以先读本地缓存再拉取最新详情页可以使用过期时间判断缓存是否有效用户个人资料要保证从服务端获取后能正确覆盖本地缓存。缓存不是后加的装饰它影响的是弱网体验和数据一致性。3.4 接口日志与问题复现进入测试阶段后会出现大量“这个页面没数据”“这里报错了”的反馈。如果 App 端没有接口日志或日志里不包含请求地址、参数、响应状态码问题基本没法定位。建议在网络层统一打印请求方法、URL、请求体摘要、响应状态码和耗时并给每个请求生成一个请求 ID。这样前后端对齐时只需要把请求 ID 给后端后端就能在同一条链路里找到日志。需要注意的是日志里不要打印完整密码、token、验证码等敏感字段否则日志文件本身会成为安全风险。4. 测试与抓包进入后半程必须补上的能力4.1 三层测试体系怎么搭测试不是为了凑覆盖率数字而是为了在修改代码时能立刻知道核心链路有没有被破坏。后半程至少要搭建三层测试。第一层是单元测试重点覆盖工具函数、日期处理、状态计算、数据解析。这类测试运行快可以作为提交代码前的本地检查。第二层是组件或 UI 测试重点是验证页面状态变化。比如登录按钮在输入为空时不可点、加载中显示 loading、接口报错时显示重试按钮。Flutter 使用integration_testAndroid 使用 EspressoiOS 使用 XCUITest都可以实现。第三层是端到端冒烟测试发布前跑一遍主流程。没有必要覆盖所有页面但注册、登录、核心业务操作、退出这四条路径必须稳定。测试的本质是把已经确认正确的行为固化下来。50% 后功能还在快速变化如果每个改动都靠人工重新检查所有页面效率会越来越低。4.2 手机 App 抓包配置抓包是排查接口请求、响应和错误码的必备手段。用 Fiddler 或 Charles 抓取手机 App 请求时基本流程如下手机和电脑连接到同一个局域网。在抓包工具中开启远程监听确认监听端口常见端口是 8888 或 8887。在手机 Wi-Fi 的高级网络配置里把 HTTP 网络入口指向电脑的局域网 IP 和抓包工具监听端口。用手机浏览器访问抓包工具提供的证书下载地址下载并安装 HTTPS 证书。iOS 在安装证书后还需要到“设置 - 通用 - 关于本机 - 证书信任设置”中开启对根证书的完全信任。Android 7.0 及以上系统默认不信任用户证书需要在测试包中确认是否放开了用户证书信任。完成这些配置后打开 App 发起请求抓包工具中就能看到完整的请求地址、请求头、请求体、响应内容和耗时。注意抓包只能用于自己负责的开发、测试和排障场景。不要在未授权环境下抓取他人数据也不要抓取生产环境中的敏感账号信息。4.3 抓包失败排查表抓包失败通常不是工具坏了而是配置链路上某个环节没有对齐。常见问题可以按下面的表格排查问题现象常见原因检查方式处理建议看不到任何请求手机流量没有经过抓包工具监听端口用手机浏览器访问一个网页看抓包工具是否有记录确认手机与电脑同一局域网端口和 IP 配置正确只有 CONNECT 请求没有内容HTTPS 证书未安装手机访问证书下载地址检查证书是否已安装重新下载并安装证书重启抓包工具Android 上 HTTPS 内容为空Android 7.0 默认不信任用户证书查看测试包是否配置network_security_config在 debug 构建中信任用户证书iOS 持续提示 SSL 错误根证书未开启完全信任查看系统证书信任设置打开证书信任开关重新启动 App请求长时间无响应电脑防火墙拦截端口查看抓包工具是否有连接记录放行抓包工具监听端口抓包配置整理完成后建议写成一页团队文档标明电脑 IP、监听端口、证书下载地址、Android 和 iOS 各自的注意事项。这样每个人拿到手机都能按同一套流程操作。4.4 崩溃与线上异常采集开发阶段靠断点和日志测试阶段靠真机和抓包但发布后只能靠崩溃采集平台。50% 这个阶段就应该接入崩溃采集而不是等上线后再补。崩溃平台能收集崩溃堆栈、设备型号、系统版本、App 版本、崩溃发生前的操作路径。接入时要特别注意 release 包的符号映射Android 混淆后需要保留 mapping 文件iOS 需要上传 dSYM 文件否则看到的是十六进制地址无法定位到具体代码。有了崩溃平台后还要设置告警。核心流程的崩溃率一旦超过阈值应该有人第一时间收到通知而不是等到应用商店用户评分下降才发现问题。5. 兼容性、签名与发布从“能跑到”到“能上架”还有距离5.1 从 MinimumOSVersion 说起开发阶段测试机通常是高系统版本很多兼容性问题到发布前才会暴露。一个典型报错是MinimumOSVersion too low. This app has a MinimumOSVersion of 13.0. Starting...这个提示的意思是当前设备系统版本低于 App 要求的最低系统版本无法启动。在 iOS 工程里最低系统版本由 Deployment Target 决定构建产物中会写入MinimumOSVersion例如keyMinimumOSVersion/key string13.0/string出现这个问题的原因通常是三种第一主工程的 Deployment Target 设置得过高第二某个第三方依赖库要求的最低系统版本高于主工程第三发布包和声明支持的系统版本不一致。合理做法是尽早确定最低支持版本不要拍脑袋。确定后要在至少一台最低版本和一台最新版本的真机上跑核心流程模拟器并不能完全代表真机行为。Android 端同理要选择合适的minSdkVersion并注意 Android 高版本的明文流量、存储权限、通知权限变化。5.2 证书、描述文件与“5 天后无法访问”iOS App 在设备上会显示类似提示You will lose access to this app in 5 days这通常意味着安装的 App 使用的开发描述文件或证书即将过期。开发构建安装到真机时系统会校验签名有效期当距离过期不足 5 天时设备会提前警告用户。处理方式不是等到 5 天后再说而是及时更新证书和描述文件然后重新构建并安装。要检查开发者账号的状态、真机 UDID 是否在描述文件里、证书是否被吊销。发布到 App Store 的正式包走的是分发证书和发布描述文件不能和开发签名混用。Android 的签名相对简单但也要注意签名文件的保存和备份。一旦签名文件丢失后续无法对已发布应用进行同签名升级。注意签名必须通过官方开发者账号体系和正规流程完成。不要使用绕过系统校验的第三方重签名方案这类方案既违反开发者协议也会给用户设备带来不可控风险。5.3 提审前要确认的兼容性项无论是上架 App Store 还是 Android 应用市场审核前都要确认一批基础材料检查项具体内容应用标识包名或应用 ID 全局唯一且不过期版本号版本号和构建号明确递增隐私政策提供可访问的隐私政策链接权限说明相机、定位、通知等权限用途说明用户协议注册、登录、支付相关协议内购配置IAP 或支付参数与后端一致App 图标与截图各尺寸图标、不同设备截图敏感功能说明账号注销、数据删除入口不能缺失这些项目看起来是运营和产品的事但技术侧要负责把入口做出来。特别是账号注销和数据删除现在很多应用市场会主动检查技术不实现的话材料再齐全也过不了关。5.4 发布前构建与回滚发布不是“打完包传上去”这么简单。建议在进入下半程后就使用 CI 完成自动构建每次提交都生成可追溯的构建号。构建产物要留存版本号要严格递增代码要用 tag 标记。上线后如果发现问题第一优先级是走回滚方案而不是在线上紧急修改代码。App 端要支持服务端下发配置开关对高风险功能做灰度控制。如果 Hermes Studio App 后期涉及多端还要设计统一的版本策略。后端接口版本、App 版本、数据迁移版本要保持一致避免出现旧 App 调新接口、新 App 调旧接口的混乱局面。6. 50% 以后最常见的四类工程问题6.1 同样的代码别人拉下来编译失败现象是代码在自己的电脑上能编译换一个同事拉取后立刻报错。优先按以下顺序排查依赖是否锁定、本地缓存是否不一致、路径是否含中文或特殊字符、Node 或 Flutter 或 JDK 版本是否与项目要求一致、环境变量是否缺失。这类问题不是代码逻辑问题而是工程环境问题。建议团队使用统一的版本管理工具并在项目文档中写清楚环境要求包括语言版本、SDK 版本、包管理器版本。6.2 真机能装但启动闪退能安装说明签名和设备兼容没问题启动闪退要分成两步看。第一步看崩溃日志第二步看具体场景。常见原因是本地数据库迁移没有做好旧版本数据表结构和新版本不一致或者某个页面依赖的字段在接口返回中缺失代码直接取了空对象属性。解决方式是打开崩溃平台找到具体堆栈然后补充对应的空数据或迁移测试。6.3 登录状态总是丢失登录态丢失通常和 token 存储、token 刷新、多设备登录三个点有关。先看 token 是否被放在了会被系统清理的地方再看 401 拦截器是否误判了其他接口最后看是否服务端在刷新 token 后吊销了旧 token。排查时使用抓包工具观察请求序列是 App 启动后第一个请求就返回 401还是某个页面业务请求返回 401这决定了问题出在存储还是刷新逻辑。6.4 发布前才发现权限或隐私材料没准备很多团队在提审前一周才开始申请权限说明、准备隐私政策结果因为材料缺失被驳回导致发布延期。建议在 50% 阶段就把权限清单整理出来当前用了哪些权限、每个权限在哪个功能里触发、是否需要额外说明。iOS 要检查Info.plist里的用途描述Android 要检查AndroidManifest.xml里的权限声明和运行时申请逻辑。7. 下半程的迭代节奏与最佳实践7.1 每个迭代都定义“完成”后半程最怕“功能做完了但不知道什么叫完成”。建议给每个迭代设定统一的完成标准代码通过评审没有未解决的阻塞问题。对应的自动化测试在 CI 上通过。核心流程在真机上验证通过。接口日志和崩溃日志可追踪。新增依赖有明确版本并已锁定。文档和配置示例已同步更新。不符合这些条件的功能不应该算进“已完成”列表。7.2 新需求能不能插队的判断规则50% 后新需求仍然会持续出现但不能每个都直接排进当前迭代。可以按三个问题判断第一这个需求是否影响核心流程如果不做会导致主流程不可用。第二这个需求是否必须在本版本发布还是可以通过配置开关或隐藏入口延后。第三这个需求是否引入新的系统权限、第三方服务或数据模型变更如果会需要额外评估依赖和安全成本。没有清晰价值的新需求放到下一个版本是更稳妥的选择。7.3 定期做一次发布演练发布演练听起来像流程事务但它能提前暴露大量真实问题。每个月做一次“从拉取代码到发布一个可安装测试包”的全过程记录耗时和失败点。演练内容包括拉