mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-17 08:46:40 +08:00
1157 lines
35 KiB
HTML
1157 lines
35 KiB
HTML
<!DOCTYPE html>
|
||
|
||
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
|
||
|
||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||
|
||
<head>
|
||
|
||
<head>
|
||
|
||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
|
||
|
||
<link rel="icon" href="/static/favicon.png">
|
||
|
||
<title>35 版本发布:软件上线只是新的开始.md.html</title>
|
||
|
||
<!-- Spectre.css framework -->
|
||
|
||
<link rel="stylesheet" href="/static/index.css">
|
||
|
||
<!-- theme css & js -->
|
||
|
||
<meta name="generator" content="Hexo 4.2.0">
|
||
|
||
</head>
|
||
|
||
|
||
|
||
<body>
|
||
|
||
|
||
|
||
<div class="book-container">
|
||
|
||
<div class="book-sidebar">
|
||
|
||
<div class="book-brand">
|
||
|
||
<a href="/">
|
||
|
||
<img src="/static/favicon.png">
|
||
|
||
<span>技术文章摘抄</span>
|
||
|
||
</a>
|
||
|
||
</div>
|
||
|
||
<div class="book-menu uncollapsible">
|
||
|
||
<ul class="uncollapsible">
|
||
|
||
<li><a href="/" class="current-tab">首页</a></li>
|
||
|
||
</ul>
|
||
|
||
|
||
|
||
<ul class="uncollapsible">
|
||
|
||
<li><a href="../">上一级</a></li>
|
||
|
||
</ul>
|
||
|
||
|
||
|
||
<ul class="uncollapsible">
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/00 开篇词 你为什么应该学好软件工程?.md.html">00 开篇词 你为什么应该学好软件工程?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/01 到底应该怎样理解软件工程?.md.html">01 到底应该怎样理解软件工程?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/02 工程思维:把每件事都当作一个项目来推进.md.html">02 工程思维:把每件事都当作一个项目来推进.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/03 瀑布模型:像工厂流水线一样把软件开发分层化.md.html">03 瀑布模型:像工厂流水线一样把软件开发分层化.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/04 瀑布模型之外,还有哪些开发模型?.md.html">04 瀑布模型之外,还有哪些开发模型?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/05 敏捷开发到底是想解决什么问题?.md.html">05 敏捷开发到底是想解决什么问题?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/06 大厂都在用哪些敏捷方法?(上).md.html">06 大厂都在用哪些敏捷方法?(上).md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/07 大厂都在用哪些敏捷方法?(下).md.html">07 大厂都在用哪些敏捷方法?(下).md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/08 怎样平衡软件质量与时间成本范围的关系?.md.html">08 怎样平衡软件质量与时间成本范围的关系?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/09 为什么软件工程项目普遍不重视可行性分析?.md.html">09 为什么软件工程项目普遍不重视可行性分析?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/10 如果你想技术转管理,先来试试管好一个项目.md.html">10 如果你想技术转管理,先来试试管好一个项目.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/11 项目计划:代码未动,计划先行.md.html">11 项目计划:代码未动,计划先行.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/12 流程和规范:红绿灯不是约束,而是用来提高效率.md.html">12 流程和规范:红绿灯不是约束,而是用来提高效率.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/13 白天开会,加班写代码的节奏怎么破?.md.html">13 白天开会,加班写代码的节奏怎么破?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/14 项目管理工具:一切管理问题,都应思考能否通过工具解决.md.html">14 项目管理工具:一切管理问题,都应思考能否通过工具解决.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/15 风险管理:不能盲目乐观,凡事都应该有B计划.md.html">15 风险管理:不能盲目乐观,凡事都应该有B计划.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/16 怎样才能写好项目文档?.md.html">16 怎样才能写好项目文档?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/17 需求分析到底要分析什么?怎么分析?.md.html">17 需求分析到底要分析什么?怎么分析?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/18 原型设计:如何用最小的代价完成产品特性?.md.html">18 原型设计:如何用最小的代价完成产品特性?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/19 作为程序员,你应该有产品意识.md.html">19 作为程序员,你应该有产品意识.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/20 如何应对让人头疼的需求变更问题?.md.html">20 如何应对让人头疼的需求变更问题?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/21 架构设计:普通程序员也能实现复杂系统?.md.html">21 架构设计:普通程序员也能实现复杂系统?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/22 如何为项目做好技术选型?.md.html">22 如何为项目做好技术选型?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/23 架构师:不想当架构师的程序员不是好程序员.md.html">23 架构师:不想当架构师的程序员不是好程序员.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/24 技术债务:是继续修修补补凑合着用,还是推翻重来?.md.html">24 技术债务:是继续修修补补凑合着用,还是推翻重来?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/25 有哪些方法可以提高开发效率?.md.html">25 有哪些方法可以提高开发效率?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/26 持续交付:如何做到随时发布新版本到生产环境?.md.html">26 持续交付:如何做到随时发布新版本到生产环境?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/27 软件工程师的核心竞争力是什么?(上).md.html">27 软件工程师的核心竞争力是什么?(上).md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/28 软件工程师的核心竞争力是什么?(下).md.html">28 软件工程师的核心竞争力是什么?(下).md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/29 自动化测试:如何把Bug杀死在摇篮里?.md.html">29 自动化测试:如何把Bug杀死在摇篮里?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/30 用好源代码管理工具,让你的协作更高效.md.html">30 用好源代码管理工具,让你的协作更高效.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/31 软件测试要为产品质量负责吗?.md.html">31 软件测试要为产品质量负责吗?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/32 软件测试:什么样的公司需要专职测试?.md.html">32 软件测试:什么样的公司需要专职测试?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/33 测试工具:为什么不应该通过QQ微信邮件报Bug?.md.html">33 测试工具:为什么不应该通过QQ微信邮件报Bug?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/34 账号密码泄露成灾,应该怎样预防?.md.html">34 账号密码泄露成灾,应该怎样预防?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
<a class="current-tab" href="/专栏/软件工程之美/35 版本发布:软件上线只是新的开始.md.html">35 版本发布:软件上线只是新的开始.md.html</a>
|
||
|
||
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/36 DevOps工程师到底要做什么事情?.md.html">36 DevOps工程师到底要做什么事情?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/37 遇到线上故障,你和高手的差距在哪里?.md.html">37 遇到线上故障,你和高手的差距在哪里?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/38 日志管理:如何借助工具快速发现和定位产品问题 ?.md.html">38 日志管理:如何借助工具快速发现和定位产品问题 ?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/39 项目总结:做好项目复盘,把经验变成能力.md.html">39 项目总结:做好项目复盘,把经验变成能力.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/40 最佳实践:小团队如何应用软件工程?.md.html">40 最佳实践:小团队如何应用软件工程?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/41 为什么程序员的业余项目大多都死了?.md.html">41 为什么程序员的业余项目大多都死了?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/42 反面案例:盘点那些失败的软件项目.md.html">42 反面案例:盘点那些失败的软件项目.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/43 以VS Code为例,看大型开源项目是如何应用软件工程的?.md.html">43 以VS Code为例,看大型开源项目是如何应用软件工程的?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/44 微软、谷歌、阿里巴巴等大厂是怎样应用软件工程的?.md.html">44 微软、谷歌、阿里巴巴等大厂是怎样应用软件工程的?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/45 从软件工程的角度看微服务、云计算、人工智能这些新技术.md.html">45 从软件工程的角度看微服务、云计算、人工智能这些新技术.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/一问一答第1期 30个软件开发常见问题解决策略.md.html">一问一答第1期 30个软件开发常见问题解决策略.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/一问一答第2期 30个软件开发常见问题解决策略.md.html">一问一答第2期 30个软件开发常见问题解决策略.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/一问一答第3期 18个软件开发常见问题解决策略.md.html">一问一答第3期 18个软件开发常见问题解决策略.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/一问一答第4期 14个软件开发常见问题解决策略.md.html">一问一答第4期 14个软件开发常见问题解决策略.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/一问一答第5期 22个软件开发常见问题解决策略.md.html">一问一答第5期 22个软件开发常见问题解决策略.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/学习攻略 怎样学好软件工程?.md.html">学习攻略 怎样学好软件工程?.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/特别放送 从软件工程的角度解读任正非的新年公开信.md.html">特别放送 从软件工程的角度解读任正非的新年公开信.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
<li>
|
||
|
||
|
||
|
||
|
||
|
||
<a href="/专栏/软件工程之美/结束语 万事皆项目,软件工程无处不在.md.html">结束语 万事皆项目,软件工程无处不在.md.html</a>
|
||
|
||
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
|
||
|
||
<div class="sidebar-toggle-inner"></div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
<script>
|
||
|
||
function add_inner() {
|
||
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
|
||
inner.classList.add('show')
|
||
|
||
}
|
||
|
||
|
||
|
||
function remove_inner() {
|
||
|
||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||
|
||
inner.classList.remove('show')
|
||
|
||
}
|
||
|
||
|
||
|
||
function sidebar_toggle() {
|
||
|
||
let sidebar_toggle = document.querySelector('.sidebar-toggle')
|
||
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
|
||
let content = document.querySelector('.off-canvas-content')
|
||
|
||
if (sidebar_toggle.classList.contains('extend')) { // show
|
||
|
||
sidebar_toggle.classList.remove('extend')
|
||
|
||
sidebar.classList.remove('hide')
|
||
|
||
content.classList.remove('extend')
|
||
|
||
} else { // hide
|
||
|
||
sidebar_toggle.classList.add('extend')
|
||
|
||
sidebar.classList.add('hide')
|
||
|
||
content.classList.add('extend')
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
function open_sidebar() {
|
||
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
|
||
sidebar.classList.add('show')
|
||
|
||
overlay.classList.add('show')
|
||
|
||
}
|
||
|
||
function hide_canvas() {
|
||
|
||
let sidebar = document.querySelector('.book-sidebar')
|
||
|
||
let overlay = document.querySelector('.off-canvas-overlay')
|
||
|
||
sidebar.classList.remove('show')
|
||
|
||
overlay.classList.remove('show')
|
||
|
||
}
|
||
|
||
|
||
|
||
</script>
|
||
|
||
|
||
|
||
<div class="off-canvas-content">
|
||
|
||
<div class="columns">
|
||
|
||
<div class="column col-12 col-lg-12">
|
||
|
||
<div class="book-navbar">
|
||
|
||
<!-- For Responsive Layout -->
|
||
|
||
<header class="navbar">
|
||
|
||
<section class="navbar-section">
|
||
|
||
<a onclick="open_sidebar()">
|
||
|
||
<i class="icon icon-menu"></i>
|
||
|
||
</a>
|
||
|
||
</section>
|
||
|
||
</header>
|
||
|
||
</div>
|
||
|
||
<div class="book-content" style="max-width: 960px; margin: 0 auto;
|
||
|
||
overflow-x: auto;
|
||
|
||
overflow-y: hidden;">
|
||
|
||
<div class="book-post">
|
||
|
||
<p id="tip" align="center"></p>
|
||
|
||
<div><h1>35 版本发布:软件上线只是新的开始</h1>
|
||
|
||
<p>你好,我是宝玉。上一章我们学习了软件测试篇,今天,我们将从版本发布这个话题开始,进入到运行维护篇的学习。</p>
|
||
|
||
<p>说到版本发布,对于很多开发人员来说,觉得是一件很简单的事情,就是将程序编译打包部署,但实际发布的时候,却经常出现发布错版本的问题,或者是发布前修改了一点代码导致上线出现 Bug 的情况发生。</p>
|
||
|
||
<p>而版本发布对于很多项目管理者来说,又是一个很纠结的事情,觉得还有很多功能没完成,很多 Bug 还没改完,害怕用户负面评价,结果时间一拖再拖,迟迟无法上线。</p>
|
||
|
||
<p>所以今天我将带你一起学习一下如何做好版本发布,保障好发布产品的质量。</p>
|
||
|
||
<h2>关于软件版本</h2>
|
||
|
||
<p>在讨论这个话题之前,我们需要了解“版本”的含义。也许你已经知道版本的含义,但是这里还是有必要明确一下,因为在不同的语境下,版本的含义也有所不同。比如产品经理会对开发人员说:“这个功能我们会放到下个版本中实现”,开发人员会对测试人员说:“这个 Bug 在昨天的版本中已经修复了”。</p>
|
||
|
||
<p>这里产品经理说的“版本”是指特定功能集合,开发人员说的“版本”是指某一次程序的构建结果。也就是说对软件版本来说,包含两部分含义,一部分代表特定功能集合,一部分代表某一次特定的代码构建结果。</p>
|
||
|
||
<p>为了明确标识软件版本,需要对版本进行编号。目前业界在软件版本的命名上,通常会采用以下方式:</p>
|
||
|
||
<blockquote>
|
||
|
||
<p>主版本号 . 子版本号.[. 修正版本号.[构建版本号]]</p>
|
||
|
||
</blockquote>
|
||
|
||
<p>比如说:1.2.1、2.0、3.0.1 build-123。</p>
|
||
|
||
<p>其中主版本号和子版本号用来标识功能变化,小的功能变化增加子版本号,大的功能变化增加主版本号。修正版本号则表示功能不变化的情况下修复 Bug,而构建版本号表示一次新的构建,这个通常由编译程序自动生成。</p>
|
||
|
||
<p>团队中对版本有了清晰的定义和良好的版本编号,在讨论版本时,你就可以根据版本号清楚地知道应该有哪些功能,属于哪一次的构建结果。在修复 Bug 或者增加功能时,开发人员也能清楚地知道代码应该加入哪个版本。在验证 Bug 时,测试人员就可以知道应该在哪个版本中验证 Bug 有没有被修复。</p>
|
||
|
||
<h2>版本发布前,做好版本发布的规划</h2>
|
||
|
||
<p>回到前面的问题,为什么有的项目管理者会在发布前感觉没准备好,害怕上线发布呢?根源上,他们还是对于功能和质量没有信心,担心发布后获得负面的评价。</p>
|
||
|
||
<p>而实际上,并不代表你需要完成所有的功能,或者没有任何 Bug,有一个完美的版本才能上线。毕竟追求完美是没有止境的,这世界上也不存在完美的软件,很多著名的软件,比如 Windows、Office、iOS 都免不了在发布后还要打补丁。</p>
|
||
|
||
<p>这里的关键在于,<strong>要在用户(或客户)的心理预期和你软件的实际情况之间,达到一种平衡,让软件的功能和质量,满足好用户的预期。</strong></p>
|
||
|
||
<p>要合理管理好用户的预期,达到好的发布效果,就需要在版本发布前先做好版本发布的规划。</p>
|
||
|
||
<p>那么,版本的发布规划,是指规划哪些内容呢?</p>
|
||
|
||
<p>**首先是规划好要发布的功能。**在发布前,搞清楚哪些是用户必须要有的功能,哪些是用户可以没有的功能。对于必须要有的功能,那么要保证软件中有这个功能才能发布,对于不是必需的功能,可以以后再逐步完善。</p>
|
||
|
||
<p>有一个经典的案例就是第一代的 iPhone,复制粘贴的功能都没有,但是在用户界面和触屏设计上做的非常好,一样取得了极大的成功。</p>
|
||
|
||
<p>然后是定义好发布的质量标准。在发布前,搞清楚你的用户对质量的容忍度如何,对哪些功能的质量要求高,对哪些功能的质量要求没那么高。对于那些用户在意的功能,要具有较高的发布标准,反之,则可以有较低的质量标准。</p>
|
||
|
||
<p>举例来说,像微博这种社交类应用软件,界面布局存在一点问题,用户是可以接受的,但是如果撰写内容的时候,因程序异常导致辛辛苦苦写的内容丢失,用户是比较难以忍受的。</p>
|
||
|
||
<p>**再有就是要设计好发布的策略。**考虑好是直接发布给所有用户?还是先让一部分用户试用?比如说可以先让内部用户使用,内部用户对软件质量问题容忍度是很高的,还可以帮助发现很多问题。</p>
|
||
|
||
<p>让一部分用户使用 Beta 版也是一个好的发布策略,当用户知道你的软件还是 Beta 版的时候,要求会比较低一点,可以接受一些不那么严重的 Bug。 还有就是采用灰度测试的发布策略,让一小部分用户先用新功能,如果没发现什么问题,再继续扩大使用的用户规模,如果有问题,也只是影响少量用户。</p>
|
||
|
||
<p>像 Gmail 在最初的几年,一直是 Beta 版本测试。还有像苹果的 iOS,用户也可以选择安装最新的 Beta 版本,可以先体验新功能,但是必须忍受系统的不稳定。</p>
|
||
|
||
<p>**最后,就是有一个综合性的版本发布计划。**在确定了要发布的功能、定义好了质量标准、设计好了发布策略,就可以制定一个综合性的版本发布计划了,确定好发布的时间点。</p>
|
||
|
||
<p>这个发布计划,不只是项目内部成员,还需要和项目之外利益相关方,比如客户、市场运营人员,大家一起确定最终的发布计划。</p>
|
||
|
||
<p>在对版本的发布做好规划后,就不用再纠结于该不该发布,该什么时候发布的问题。</p>
|
||
|
||
<p>有功能没完成没关系,关键要看这个功能是不是必须要有;有 Bug 没有修复完成,是不是影响发布,要看这些 Bug 是不是影响发布的质量标准;还可以采用一些像 Beta 版、小规模用户试用的发布策略,降低用户对功能和质量的预期。</p>
|
||
|
||
<h2>规范好发布流程,保障发布质量</h2>
|
||
|
||
<p>在规划好发布的版本后,要发布版本似乎是一件很简单的事,就是将源代码编码、部署。</p>
|
||
|
||
<p>但发布版本,可能并不是像你想的那么容易,这其中有几个需要注意的问题。</p>
|
||
|
||
<p>首先是必须保证要编译部署的是正确的版本。虽然一般来说,开发人员不会犯这样的错误,但是如果发布了错误的版本,后果可能很严重,所以要引起足够重视。比如说当年拼多多造成了几千万损失的薅羊毛事件,就是错误发布了一个测试用的无门槛券,导致被大量滥用。</p>
|
||
|
||
<p>然后要保证版本稳定可靠。如果你有开发经验的话,应该知道开发软件,一个常识就是每一次对代码的修改,都可能导致新的 Bug 产生。如果你的代码库在发布之前还一直在增加新的功能或者是不停地修复 Bug,那么质量是难以稳定下来的。</p>
|
||
|
||
<p>再就是要在发布失败后能回滚。没有谁能保证程序发布后没有严重问题,所以最保险的办法就是要在部署后,如果发现发布的版本出现严重问题,就应该对程序进行回滚操作,恢复到部署之前的状态。即使有些不可逆的升级,也需要事先做好应对措施,比如发布公告,停止服务,尽快修复。</p>
|
||
|
||
<p>针对这些问题,已经有些好的实践,比如说代码冻结、Bug 分级、回归测试等可以降低发布风险,保障发布产品的质量。在《12 流程和规范:红绿灯不是约束,而是用来提高效率》这篇文章中,我也提到了:流**程和规范能将好的实践标准化流程化,让大家可以共享经验。**所以在版本发布上,我们也可以制定合理的流程,来应用这些好的实践,保证发布的质量。</p>
|
||
|
||
<p>那么一般大厂都是什么样的发布流程呢?下面这个流程可以作为一个参考。</p>
|
||
|
||
<ul>
|
||
|
||
<li>在发布之前要做代码冻结。</li>
|
||
|
||
</ul>
|
||
|
||
<p>什么是代码冻结呢?就是在发布之前,对于要发布的版本,在源代码管理工具中,专门创建一个 release 分支,然后对于这个分支的代码,冻结功能的修改,不接受新功能的增加,甚至重要性不高的 Bug 都不修改,只修复重要的 Bug。</p>
|
||
|
||
<p>由于严格的控制代码的修改,这样可以让版本的质量逐步趋于稳定。</p>
|
||
|
||
<ul>
|
||
|
||
<li>对代码冻结后发现的 Bug 要分级</li>
|
||
|
||
</ul>
|
||
|
||
<p>在代码冻结后,可能还存在一些 Bug,测试的过程中也会新增一些 Bug。代码冻结的原则就是尽可能减少代码的修改,避免引起不稳定。所以对于这些 Bug,要有一个简单的分级:是否在发布前修改,还是留在发布后再修改。</p>
|
||
|
||
<p>至于如何对一个 Bug 分级,这需要项目负责人和产品负责人一起确认。</p>
|
||
|
||
<ul>
|
||
|
||
<li>每次修复 Bug 后,发布新的候选版本</li>
|
||
|
||
</ul>
|
||
|
||
<p>进入代码冻结后,开发人员还需要对一些 Bug 进行修复,每一次修复完 Bug 后,就要生成一个新的候选发布版本,比如说 1.1 RC1、1.1 RC2。</p>
|
||
|
||
<p>关于生成发布版本,现在比较流行的做法是和持续集成系统整合,完全自动化。也就是在自动化测试通过之后,会自动构建,生成各个环境的发布版本。这样好处是,可以避免人为失误导致的错误,另外程序的配置管理做好了的话,只要测试环境的版本在测试环境测试没问题,那么就可以认为在生产环境的版本也是正常的。</p>
|
||
|
||
<p>自动化构建,生成发布版本并不复杂,各个语言都有成熟的方案,如果你还不了解的话,可以通过搜索引擎搜索关键字:“[对应平台] 自动打包”,例如搜索“iOS 自动打包”、“iOS build automation”这样的关键字。</p>
|
||
|
||
<p>其中稍微有点麻烦的就是如何应用不同环境下的不同配置,比如说测试环境连测试环境服务器,生产环境连生产环境服务器。有关程序配置管理部分,可以参考这篇文章:<a href="http://insights.thoughtworkers.org/large-project-configuration-management/">大型项目程序配置管理演化之路</a></p>
|
||
|
||
<ul>
|
||
|
||
<li>每次部署新的候选发布版本后,要做回归测试</li>
|
||
|
||
</ul>
|
||
|
||
<p>在每次开发人员部署新的候选发布版本到测试环境后,还需要做一次回归测试。也就是说在 Bug 修复完,对主要流程要重新测试一遍,同时还要对之前确认过的 Bug 再确认一遍,以确保 Bug 确实修复了,并且没有引入新的 Bug。</p>
|
||
|
||
<p>如果当前候选发布版本达到版本发布的质量标准后,就可以准备发布了。</p>
|
||
|
||
<ul>
|
||
|
||
<li>申请上线发布</li>
|
||
|
||
</ul>
|
||
|
||
<p>上线发布是一件很严谨的事,所以在正式上线发布前,通常还需要有一个申请和审批的流程。审批的主要目的是要有人或者有部门统筹对所有的上线发布有一个全面的了解和控制,避免上线过于随意导致问题,避免和其他部门的上线冲突。</p>
|
||
|
||
<ul>
|
||
|
||
<li>部署发布</li>
|
||
|
||
</ul>
|
||
|
||
<p>如果已经实现了自动化,部署发布应该是非常简单的一步。如果还没有自动化部署发布,也需要事先将详细的操作步骤写下来,避免部署发布时发生纰漏,这样在实际部署发布时,按照事先写好的步骤操作就不容易出现错误。</p>
|
||
|
||
<ul>
|
||
|
||
<li>上线后的测试</li>
|
||
|
||
</ul>
|
||
|
||
<p>项目上线后,测试人员需要马上对已经上线的版本做一个主要功能的测试,以确保线上运行正常。如果做好了数据监控,还同时要对一些关键数据进行监控,例如服务器 CPU 利用率、内存占用率、服务出错率等数据。</p>
|
||
|
||
<p>如果万一发现版本上线后出现问题,需要考虑按照事先准备好的回滚方案进行回滚操作,尽量将损失降到最低。通常不到万不得已,不建议马上对问题打补丁进行修复。因为哪怕很小的代码修改,都可能会引入新的 Bug。而重新做一遍回归测试,耗时会比较长。</p>
|
||
|
||
<p>以上就是版本发布的一个常见流程,你也可以基于这个流程制定适合你项目的流程,让你的版本发布更加稳定可靠。</p>
|
||
|
||
<h2>软件上线只是新的开始</h2>
|
||
|
||
<p>当你的软件上线后,这不代表你的项目就结束了,可能这才只是新的开始。</p>
|
||
|
||
<p>用户在使用你的产品的时候,可能会遇到一些 Bug 或者是有一些建议,所以需要给用户反馈的渠道,让用户可以有途径对于 Bug 或者功能去反馈。通过收集用户的反馈,可以进一步完善你的软件产品。</p>
|
||
|
||
<p>只是靠用户主动反馈问题还是不够的,需要主动的对发布的版本进行监控,比如说要收集 App Crash 的 Log、监控服务器资源占用情况、监控 API 出错的比例、监控网页响应的速度等数据。当发现数据异常时,很可能说明发布的版本是有问题的,需要及时的应对,回滚版本或者发布新的更新补丁。</p>
|
||
|
||
<p>有关线上监控和报警的内容,将会在后续我们课程《38 日志管理:如何借助工具快速发现和定位产品问题 ?》中带你继续学习。</p>
|
||
|
||
<p>不管怎么样,软件成功上线了都是一件值得祝贺的事情,同时也是时候回顾总结一下整个项目过程了,关于这一点,我也会在后续专栏文章《39 项目总结:做好项目复盘,把经验变成能力》中跟你一起探讨如何做好项目总结复盘,把经验变成能力。</p>
|
||
|
||
<h2>总结</h2>
|
||
|
||
<p>今天带你一起学习了版本发布的相关知识。做好版本发布,关键在于版本发布前做好版本发布的规划,以及采用一个科学的发布流程。</p>
|
||
|
||
<p>版本规划,其实就是通过合理的规划,尽可能的让软件的功能和质量,满足好用户的预期。所以一方面要尽可能提供应有的功能和保证质量,另一方面也可以通过合理的发布策略,例如 beta 测试,降低用户预期。</p>
|
||
|
||
<p>通过规范的发布流程,可以确保要发布的版本正确以及发布质量的稳定。流程的关键在于发布前要对代码冻结,避免发布前频繁修改代码引入新的 Bug,同时在每次修复 Bug 后,要做回归测试保证 Bug 被修复以及没有引入新的 Bug,上线后还要对线上版本再一次测试,确保没有问题。整个流程中一些手工部署发布的操作应该尽可能自动化。</p>
|
||
|
||
<p>最后,软件上线只是新的开始,还需要收集用户的反馈,对线上服务进行监控和预警,对整个版本的开发过程进行总结回顾。</p>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div>
|
||
|
||
<div style="float: left">
|
||
|
||
<a href="/专栏/软件工程之美/34 账号密码泄露成灾,应该怎样预防?.md.html">上一页</a>
|
||
|
||
</div>
|
||
|
||
<div style="float: right">
|
||
|
||
<a href="/专栏/软件工程之美/36 DevOps工程师到底要做什么事情?.md.html">下一页</a>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||
|
||
</div>
|
||
|
||
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997c8a1d403cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
|
||
|
||
</body>
|
||
|
||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||
|
||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
|
||
|
||
<script>
|
||
|
||
window.dataLayer = window.dataLayer || [];
|
||
|
||
|
||
|
||
function gtag() {
|
||
|
||
dataLayer.push(arguments);
|
||
|
||
}
|
||
|
||
|
||
|
||
gtag('js', new Date());
|
||
|
||
gtag('config', 'G-NPSEEVD756');
|
||
|
||
var path = window.location.pathname
|
||
|
||
var cookie = getCookie("lastPath");
|
||
|
||
console.log(path)
|
||
|
||
if (path.replace("/", "") === "") {
|
||
|
||
if (cookie.replace("/", "") !== "") {
|
||
|
||
console.log(cookie)
|
||
|
||
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
setCookie("lastPath", path)
|
||
|
||
}
|
||
|
||
|
||
|
||
function setCookie(cname, cvalue) {
|
||
|
||
var d = new Date();
|
||
|
||
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
|
||
|
||
var expires = "expires=" + d.toGMTString();
|
||
|
||
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
|
||
|
||
}
|
||
|
||
|
||
|
||
function getCookie(cname) {
|
||
|
||
var name = cname + "=";
|
||
|
||
var ca = document.cookie.split(';');
|
||
|
||
for (var i = 0; i < ca.length; i++) {
|
||
|
||
var c = ca[i].trim();
|
||
|
||
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
|
||
|
||
}
|
||
|
||
return "";
|
||
|
||
}
|
||
|
||
|
||
|
||
</script>
|
||
|
||
|
||
|
||
</html>
|
||
|