炼金术(8): 开发和发布的并行
在一个技术群里,遇到一个网友提了一个问题,这种场景我曾经遇到过,提供一种解法,这需要去构建,但是如果需要的是【立即有效】的解法,可能不存在。这个方式,最小的情况下3人的迷你小team就可以实施,所以可以说是一个面向小企业的团队的介绍。麻雀虽小,五脏俱全,要用好的调度、原则、自动化工具带来效率和自由。
一个网友的问题:
为了赶时间,dev和pub并行,我一天发五六版,不是在发版就是在发版的路上,就是发版太频繁 导致我没时间开发。
问题其实在于业务方,业务为了给领导看,我软件没做好,他们就用,我现在一边开发一边维护一个线上版本,他们还在往里录入数据,导致的结果就是,问题要马上解决,解决后要发版本然后我还在开发。
怎么办?
首先,源代码的分支,和部署的环境需要隔离。
- 源代码要有不同分支,发布的分支上,只修复BUG。开发的分支是新功能。
- 部署的环境要有三套,一套是开发的,一套是测试的,一套是正式的。
- 请使用好的git版本线查看工具,例如sublimemerge
你在Git的开发分支上开发新功能,部署的时候只能部署到开发环境。
你在Git的发布分支上修复BUG,部署的时候先部署到测试环境测试,测试验证通过了再发布到正式环境。
其次,工具链要做成自动化:
- 配置不同环境的部署信息,例如用JSON配置好某个环境的所有依赖的配置信息+账号密码,这个配置文件不提交到git仓库。
- 编写部署的脚本,一键部署某个Git分支的当前代码到某个部署环境上去。
- 如果进一步,可以将自动化部署配置到内部的持续集成(CI)工具上,但是,这要仔细考虑密码的存储问题。确保安全。此处可以进一步考虑。
接着,问题应该有issue管理软件整理
- 应该用一个issue管理软件管理客户提交的问题。例如,redmine、jira、teambition等。
- 在issue管理软件上创建不同的分支标记,例如1.1.1表示发布的了版本,1.1.2表示正在开发新功能的内部版本。
- 客户反馈的问题,应该添加到1.1.1版本的issue列表下。内部新开发功能的问题应该放在1.1.2版本上。
然后,人员上调整下。
- 配置一个专门的人负责和对方对接,开发不要直接和对方对接,让那个人汇总客户的问题,再提交到issue管理器软件里的1.1.1上。
- 开发如果对某些issue有问题,应该直接给这个专门负责对接的人反馈,由他/她统一跟对方协商。
- 开发人员处于「开发密集型」,无法同时应付和对方的对接和反馈问题上,这样是没法开发的。
还有,如何修复issue
- 测试需要在内部的测试环境上按照对方提交的操作步骤重现(reproduct)问题。因此,对接人需要与对方确认清楚问题出现的必要环境,是否是必现的,还是偶发的。
- 因此,需要内部搭建和对方尽量一致的测试环境,测试环境应该与开发环境能并行工作,避免开发人员占用资源时,测试无法进行测试。
- issue重现后,开发介入诊断问题,分析问题,找到原因,在issue管理软件里应该写下原因,如果原因不明,就不是【修复】,而只是【猜测】。
- 修复后,应该更改issue的状态,测试介入回归测试。
- 测试通过后,与对方沟通,确认发布到测试环境。对方在测试环境下验证通过确认后,可以发布到正式环境。
- issue列表多的时候,要标记不同的【优先级】,如果一个问题处理在一半中,如果估计时间不是阻塞的,要处理完再处理其他问题,如果是阻塞的,就要在Git上创建一个分支去处理它,这样如果不能立刻修复,可以暂时切出去修复其他更容易修复的问题。
- 如果issue很多,开发人员应该不只一个,在能力允许的范围内,尽可能的把issue分发给对应的人修复,让开发并行起来。
最后,控制周期
- 很多issue提交后,对接人员整理的issue列表,要根据2周为单位能处理多少issue,根据优先级分类,整理出2周为周期的里程碑节点,确认2周内主要解决这些issue,其他的必须延后。
- 这个分类和划分可能一开始不准确,但是要坚持这么做,多个里程碑后就会获得足够的经验来支撑作出更合理的控制。
- 尽量控制开发周期,不能用掉所有时间,例如在2/4-3/4时间内完成开发,预留内部测试的时间。
如果,项目的某些部分有严重的技术债务。技术债务一般是指:
- 设计上的某些错误,导致问题不断;
- 或者构架上的不合理,导致某些功能的增加非常费劲,成了瓶颈;
- 或者代码实现质量上出现严重的意大利面条,不论是BUG的追踪分析还是功能的增加都异常困难,或者会拆东墙补西墙
- 那么此时应该申请一个2-3周的【重构里程碑】,偿还技术债务。
- 偿还技术债务不是客户提的需求,但是在必要的时候,它就是必要的,软件最终要有质量才能提供稳定的现有功能,以及可维护的状态。
考虑小工具。
- 项目中,应该提供一组便利的工具,这些工具,每个用来独立的诊断一个特定的问题是否能正常工作。这在排除问题的过程中有用,并且能节省你的时间。
- 这些工具应该能方便的运行,例如做成windows下的bat运行,*nix上的sh脚本运行。
- 提供切换到【调试模式】和【非调试模式】的便捷工具、菜单等。在【调试模式】里,应该可以方便的查看日志。如果是UI程序,调试模式下,应该可以在UI上展示一些关键的诊断信息。注意这里说的不是在IDE里的【调试】,而是发布后运行时可切换。
编写良好的日志。
- 程序代码的日志应该根据不同模块,提编写同Tag标记的日志行。例如[module1][module2]…,这样便于你在查看日志时通过Tag来搜索。
- 如果某个模块A【向下】调用了模块B,那么A的日志里应该包含
[tagb][taga]
,这样B模块可以看到自己内部的日志以及自己被A调用的日志。A模块可以看到自己的日志,以及自己调用B的日志。如果A同时调用了C,[tagc][taga]
将只会看到A和C之间有关系的日志。诸如此类。 - 严格区分日志级别,一般来说,WARN、INFO、ERROR、三种是重要的区分。更简化的,INFO和ERROR两种区分就能覆盖大部分问题。
- 程序的内部充满了不同的控制逻辑,一般来说if-else及其嵌套,应该在所有重要的分支里都加好日志。漏掉的那个没写日志的分支很可能实际出现问题,但是你的日志丢失了,无法确认问题。
- 如果是一个循环,或者一个反复执行的定时器,此类日志要小心控制。同样的日志太多可能影响性能,也可能不利于诊断。
- 日志库应该支持日志写文件,以及日志文件个数的控制,超出配置的大小后,要循环覆盖。
- 应该提供【上传日志】的能力,当现场出现问题时,能上传日志到日志服务器。应该提供开发工具【下载日志】,下载日志去分析问题。
- 请使用合适的工具分析日志。例如,合理使用*nix下的cat/grep等命令过滤日志。如果是使用编辑器,一种方式是用cat把所有日志写入到一个日志文件,用sublime text打开,装一个它的扩展,使得你可以方便的在[ctrl+k][ctrl+s]后,输入关键字,sublime text将自动将过滤的日志在一个新的临时文件标签页里打开,这很方便做各种过滤和分析。可以说是grep的便利ui版。
项目是可以迭代的,这是基于:
- 如果我们知道一些已知【事实数据】,在这些【事实数据】支撑下,我们能做出假设,编写程序。
- 如果程序不能良好运行,那么它可能是我们对【事实数据】的分析不周密,漏掉了分支,这种是可以修复的。修复之后,这个程序处于【比之前更正确】的状态,有序的做这个过程,可以让程序对已知的【事实数据】的分支覆盖率达到足够的程度,例如98%。
- 如果出现新的【事实数据】,那么原来的覆盖率就降低了,新数据会对原来的假设有交叉,交叉的那些地方要重新考虑。这需要一个过程去再次达到平衡。
- 在这个过程中,程序运行所依赖环境的事实数据,以及基于此所作出的分支,达到了一定程度后,测试覆盖率足够后,例如98%,程序就应该处于98%情况下可以确定能正确运行。如果出现了意外的不在考虑内的新事实数据,程序有很小的概率会不正确工作。
- 在这种对数据足够的理解、分支的足够的分析、测试的足够覆盖率下。程序可以被【有信心】的发布。
这个过程,本身就是一个程序,如果要让整个过程有效率、有序收敛,越是issue来的猛烈,越要遵守原则,守住底线。
—end—