mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-09-27 05:36:42 +08:00
478 lines
34 KiB
HTML
478 lines
34 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>06 大厂都在用哪些敏捷方法?(上).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 开篇词 你为什么应该学好软件工程?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/01 到底应该怎样理解软件工程?.md.html">01 到底应该怎样理解软件工程?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/02 工程思维:把每件事都当作一个项目来推进.md.html">02 工程思维:把每件事都当作一个项目来推进</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/03 瀑布模型:像工厂流水线一样把软件开发分层化.md.html">03 瀑布模型:像工厂流水线一样把软件开发分层化</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/04 瀑布模型之外,还有哪些开发模型?.md.html">04 瀑布模型之外,还有哪些开发模型?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/05 敏捷开发到底是想解决什么问题?.md.html">05 敏捷开发到底是想解决什么问题?</a>
|
||
</li>
|
||
<li>
|
||
<a class="current-tab" href="/专栏/软件工程之美/06 大厂都在用哪些敏捷方法?(上).md.html">06 大厂都在用哪些敏捷方法?(上)</a>
|
||
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/07 大厂都在用哪些敏捷方法?(下).md.html">07 大厂都在用哪些敏捷方法?(下)</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/08 怎样平衡软件质量与时间成本范围的关系?.md.html">08 怎样平衡软件质量与时间成本范围的关系?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/09 为什么软件工程项目普遍不重视可行性分析?.md.html">09 为什么软件工程项目普遍不重视可行性分析?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/10 如果你想技术转管理,先来试试管好一个项目.md.html">10 如果你想技术转管理,先来试试管好一个项目</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/11 项目计划:代码未动,计划先行.md.html">11 项目计划:代码未动,计划先行</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/12 流程和规范:红绿灯不是约束,而是用来提高效率.md.html">12 流程和规范:红绿灯不是约束,而是用来提高效率</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/13 白天开会,加班写代码的节奏怎么破?.md.html">13 白天开会,加班写代码的节奏怎么破?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/14 项目管理工具:一切管理问题,都应思考能否通过工具解决.md.html">14 项目管理工具:一切管理问题,都应思考能否通过工具解决</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/15 风险管理:不能盲目乐观,凡事都应该有B计划.md.html">15 风险管理:不能盲目乐观,凡事都应该有B计划</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/16 怎样才能写好项目文档?.md.html">16 怎样才能写好项目文档?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/17 需求分析到底要分析什么?怎么分析?.md.html">17 需求分析到底要分析什么?怎么分析?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/18 原型设计:如何用最小的代价完成产品特性?.md.html">18 原型设计:如何用最小的代价完成产品特性?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/19 作为程序员,你应该有产品意识.md.html">19 作为程序员,你应该有产品意识</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/20 如何应对让人头疼的需求变更问题?.md.html">20 如何应对让人头疼的需求变更问题?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/21 架构设计:普通程序员也能实现复杂系统?.md.html">21 架构设计:普通程序员也能实现复杂系统?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/22 如何为项目做好技术选型?.md.html">22 如何为项目做好技术选型?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/23 架构师:不想当架构师的程序员不是好程序员.md.html">23 架构师:不想当架构师的程序员不是好程序员</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/24 技术债务:是继续修修补补凑合着用,还是推翻重来?.md.html">24 技术债务:是继续修修补补凑合着用,还是推翻重来?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/25 有哪些方法可以提高开发效率?.md.html">25 有哪些方法可以提高开发效率?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/26 持续交付:如何做到随时发布新版本到生产环境?.md.html">26 持续交付:如何做到随时发布新版本到生产环境?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/27 软件工程师的核心竞争力是什么?(上).md.html">27 软件工程师的核心竞争力是什么?(上)</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/28 软件工程师的核心竞争力是什么?(下).md.html">28 软件工程师的核心竞争力是什么?(下)</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/29 自动化测试:如何把Bug杀死在摇篮里?.md.html">29 自动化测试:如何把Bug杀死在摇篮里?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/30 用好源代码管理工具,让你的协作更高效.md.html">30 用好源代码管理工具,让你的协作更高效</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/31 软件测试要为产品质量负责吗?.md.html">31 软件测试要为产品质量负责吗?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/32 软件测试:什么样的公司需要专职测试?.md.html">32 软件测试:什么样的公司需要专职测试?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/33 测试工具:为什么不应该通过QQ微信邮件报Bug?.md.html">33 测试工具:为什么不应该通过QQ微信邮件报Bug?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/34 账号密码泄露成灾,应该怎样预防?.md.html">34 账号密码泄露成灾,应该怎样预防?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/35 版本发布:软件上线只是新的开始.md.html">35 版本发布:软件上线只是新的开始</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/36 DevOps工程师到底要做什么事情?.md.html">36 DevOps工程师到底要做什么事情?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/37 遇到线上故障,你和高手的差距在哪里?.md.html">37 遇到线上故障,你和高手的差距在哪里?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/38 日志管理:如何借助工具快速发现和定位产品问题 ?.md.html">38 日志管理:如何借助工具快速发现和定位产品问题 ?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/39 项目总结:做好项目复盘,把经验变成能力.md.html">39 项目总结:做好项目复盘,把经验变成能力</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/40 最佳实践:小团队如何应用软件工程?.md.html">40 最佳实践:小团队如何应用软件工程?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/41 为什么程序员的业余项目大多都死了?.md.html">41 为什么程序员的业余项目大多都死了?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/42 反面案例:盘点那些失败的软件项目.md.html">42 反面案例:盘点那些失败的软件项目</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/43 以VS Code为例,看大型开源项目是如何应用软件工程的?.md.html">43 以VS Code为例,看大型开源项目是如何应用软件工程的?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/44 微软、谷歌、阿里巴巴等大厂是怎样应用软件工程的?.md.html">44 微软、谷歌、阿里巴巴等大厂是怎样应用软件工程的?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/45 从软件工程的角度看微服务、云计算、人工智能这些新技术.md.html">45 从软件工程的角度看微服务、云计算、人工智能这些新技术</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/一问一答第1期 30个软件开发常见问题解决策略.md.html">一问一答第1期 30个软件开发常见问题解决策略</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/一问一答第2期 30个软件开发常见问题解决策略.md.html">一问一答第2期 30个软件开发常见问题解决策略</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/一问一答第3期 18个软件开发常见问题解决策略.md.html">一问一答第3期 18个软件开发常见问题解决策略</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/一问一答第4期 14个软件开发常见问题解决策略.md.html">一问一答第4期 14个软件开发常见问题解决策略</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/一问一答第5期 22个软件开发常见问题解决策略.md.html">一问一答第5期 22个软件开发常见问题解决策略</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/学习攻略 怎样学好软件工程?.md.html">学习攻略 怎样学好软件工程?</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/特别放送 从软件工程的角度解读任正非的新年公开信.md.html">特别放送 从软件工程的角度解读任正非的新年公开信</a>
|
||
</li>
|
||
<li>
|
||
<a href="/专栏/软件工程之美/结束语 万事皆项目,软件工程无处不在.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>06 大厂都在用哪些敏捷方法?(上)</h1>
|
||
<p>你好,我是宝玉,我今天分享的主题是:大厂都在用哪些敏捷方法?我将分为上下两篇,来与你一起讨论这个话题。</p>
|
||
<p>在我还是一个野路子程序员,到处接私活做网站时,就开始好奇:大厂都是怎么开发软件项目的?直到毕业后,我前前后后加入了若干大中小型企业,包括这些年在美国高校、公司的一些经历,对大厂的项目开发有了比较多的了解。</p>
|
||
<p>其实大厂做项目也没有什么特别的,无非就是工程中常见的“分而治之”的策略:<strong>大项目拆成小项目,大服务拆成小服务,大团队拆成小团队。</strong></p>
|
||
<p>服务之间通过商定好的标准协议进行通信,架构上将大的服务拆分隔离成微服务,大团队按照业务或者服务拆分成小组,按照一定的流程规范保障协作。最终,各个小组要负责的内容其实就不多了。</p>
|
||
<p>就像淘宝这种网站,不需要一个庞大的项目组,通过逐级分拆,一个小组可能就只需要负责一个页面中的一个小模块。</p>
|
||
<p>所以,也要归功于现在微服务、容器等新技术,可以将复杂的业务逐级拆分,让很多公司能真正敏捷起来。</p>
|
||
<p>在上一篇文章中,我有提到,团队要实施敏捷,不仅要小,还要组织扁平化。相对来说,美国的互联网大企业做的还是很不错的,组织架构都很扁平,工程师地位很高。</p>
|
||
<p>这些年,国内工程师地位应该也有很大提升,组织也在向扁平化发展。前些天我也看到阿里工程师写的一篇文章<a href="https://zhuanlan.zhihu.com/p/34024621">《敏捷开发的根本矛盾是什么?从业十余年的工程师在思考》</a>,对这个问题有精彩的论述。</p>
|
||
<p>下面,我就带你一起看看,大厂具体是怎么应用敏捷方法的。</p>
|
||
<h2>和敏捷开发相关的主要流程规范</h2>
|
||
<p>大厂里流程规范很多,最开始你会不喜欢它们,后来会离不开它们。</p>
|
||
<blockquote>
|
||
<p>这些墙很有趣。刚入狱的时候,你痛恨周围的高墙;慢慢地,你习惯了生活在其中;最终你会发现自己不得不依靠它而生存。这就叫体制化。——《肖申克的救赎》</p>
|
||
</blockquote>
|
||
<p>这里,我简单将其中和敏捷开发相关的流程介绍一下。</p>
|
||
<h3>一切工作任务围绕 Ticket 开展</h3>
|
||
<p>早些年的项目开发,都是围绕着项目计划开展的,把甘特图打印贴在墙上,方便团队成员看项目进展到什么地步了。自从敏捷化后,开始变成了看板。</p>
|
||
<p>所谓的看板,就是把白板分成几个栏,每一栏为一类,分别写着“To Do(待选取)”、“In Progress(进行中)”、“Done(完成)”等,再把工作任务变成一个个五颜六色的即时贴,根据状态贴在不同的栏下面。</p>
|
||
<p><img src="assets/33752b50f5363233581263e882e19aa9.jpg" alt="img" /></p>
|
||
<p>慢慢的物理的看板变成了电子看板,通过各种项目管理软件来管理跟踪这些任务,即时贴也变成了 Ticket(也有叫 Issue 的)。逐渐的,所有与开发相关的任务也都和 Ticket 挂钩了:</p>
|
||
<ul>
|
||
<li>
|
||
<p>报一个 Bug,提交一个 Ticket ;</p>
|
||
</li>
|
||
<li>
|
||
<p>提一条需求,提交一个 Ticket ;</p>
|
||
</li>
|
||
<li>
|
||
<p>要重构一下代码,提交一个 Ticket 。</p>
|
||
</li>
|
||
</ul>
|
||
<p>看板这种基于 Ticket 来管理跟踪任务的方式,看起来繁琐,但确实是很高效的一种方式。</p>
|
||
<ul>
|
||
<li>
|
||
<p>每一个任务的状态都可以被跟踪起来:什么时候开始做的,谁在做,做完没有。</p>
|
||
</li>
|
||
<li>
|
||
<p>整个团队在做什么一目了然。</p>
|
||
</li>
|
||
<li>
|
||
<p>Ticket 和敏捷开发中的 Backlog(任务清单)正好结合起来,通过 Ticket 可以收集管理整个项目的 Backlog 和当前 Sprint(迭代)的 Backlog。</p>
|
||
</li>
|
||
</ul>
|
||
<p>有了看板后,大家每天上班第一件事就是打开看板,看看当前 Sprint 还有哪些 Ticket 没有完成,哪些已经完成,哪些正在进行中,非常直观。</p>
|
||
<p>作为项目成员来说,做完手头的事情也不用去问项目经理该干什么事情了,直接从 To Do 栏选一条 Ticket 做就是了;对于项目经理,看看 To Do 栏还有多少没有被选取,就知道还剩多少 Ticket 没完成,看看 In Progress 栏就知道哪些 Ticket 正在进行中。</p>
|
||
<p>如果有 Ticket 在这一栏待太久或者这一栏 Ticket 太多,那可能就有风险了,就可以及时介入。</p>
|
||
<p>对于项目管理软件和 Ticket,我在后面章节中还会有进一步介绍。</p>
|
||
<h3>基于 Git 和 CI 的开发流程</h3>
|
||
<p>如果你的团队应用瀑布模型来开发,大概会有两大烦恼:<strong>代码不稳定和部署太麻烦。</strong></p>
|
||
<p>早些年虽然也用源代码管理,但是大家都是在 master(主干)上开发的,所以 master 的代码特别不稳定,一不小心就可能被人签入了不稳定的代码。所以在上线前,有一段时间叫“代码冻结期”,意思就是这期间,除非是紧急修复,否则谁都不能往上面提交代码。</p>
|
||
<p>还有,测试环境的部署也是个老大难问题,尤其是在服务较多时,编译要注意各种依赖和环境的配置。所以更新测试环境是个大工程,以至于当年我在飞信的时候,专门有人负责部署测试环境。</p>
|
||
<p>上面的“代码冻结”和“专人部署”方案,可一点都不敏捷。所以团队想要敏捷起来,一定要解决代码不稳定和部署太麻烦这两个大问题。</p>
|
||
<p>好在基于 Git 的开发流程结合 CI 的自动测试部署,很完美的解决了这两大问题。</p>
|
||
<p>Git 本来只是源代码管理工具,但是其强大的分支管理和灵活的权限控制,结合一定的开发流程,却可以帮助你很好的控制代码质量。</p>
|
||
<p>我们假设现在 master 的代码是稳定的,那么怎么保证新加入的代码也稳定呢?</p>
|
||
<p>答案就是代码审查(Code Review)和自动化测试。如果代码有严格的审查,并且所有自动化测试代码都能测试通过,那么可以认为代码质量是可靠的。当然前提是自动化测试代码要有一定的覆盖比率。</p>
|
||
<p>关于这点,对于大厂来说倒不是什么问题,正规的项目组对于代码审查和自动测试代码的覆盖率都有严格的要求。现在还有一个问题,就是如何在合并到 master 之前把代码审查和自动化测试做好呢?</p>
|
||
<p>简单来说,就是每次要往 master 添加内容,不是直接提交代码到 master,而是先基于当前稳定的 master,克隆一个 branch(分支)出来,基于 branch 去开发,开发完成后提交一个 PR(Pull Request,合并请求)。</p>
|
||
<p>图片来源:VSCode项目PR</p>
|
||
<p>PR 提交后,就可以清楚的看出来代码做了哪些改动,其他人就可以针对每一行代码写评论提出修改意见。如果确认代码没问题了,就可以通过代码审查。</p>
|
||
<p>接下来还剩下自动化测试的问题。这时候该 CI (持续集成)出场了。</p>
|
||
<p>如果你不了解 CI 是什么,可以把它想象成一个机器人,每次你提交一个 PR(严格来说是 Commit,这里略作简化)到源代码服务器,这个机器人马上就知道了。</p>
|
||
<p>然后它创建一个干净的运行环境,把你提交的代码下载下来,再下载安装所有依赖项,然后运行你的所有测试代码,运行完后,把测试结果报告给你。测试结果直观的反馈在 PR 上,绿色表示通过,红色表示不通过。</p>
|
||
<p>图片来源:Video-React项目PR</p>
|
||
<p>关于 Git 和 CI,我在之后的文章中会展开讲解,这里只是为了展现敏捷开发方法的流程。另外,阮一峰老师写过两篇文章,<a href="https://www.ruanyifeng.com/blog/2015/12/git-workflow.html">《Git 工作流程》</a>,<a href="https://www.ruanyifeng.com/blog/2015/09/continuous-integration.html">《持续集成是什么?》</a>,你也可以先行阅读了解。</p>
|
||
<p>至此,代码审查和自动测试的问题都解决了。当一个 PR 代码审查通过,以及 CI 通过了所有自动化测试,就可以合并到 master 了,而且我们也可以认为合并到 master 后的代码也是稳定的。</p>
|
||
<p>至于自动部署测试环境,反倒是简单,就是 CI 这个机器人,在你代码合并到 master 的时候,再次运行自动化测试代码,测试通过后直接运行自动部署的脚本,把 master 代码部署到开发环境或测试环境上。</p>
|
||
<p><img src="assets/be26482df2715220b0dc2f90f034bd12.jpg" alt="img" /></p>
|
||
<p>在这里以一个开发任务为例,大致讲解一下应用敏捷开发方法的基本开发流程:</p>
|
||
<ul>
|
||
<li>
|
||
<p>把要开发的 Ticket 从“To Do”栏移动到“In Progress”栏;</p>
|
||
</li>
|
||
<li>
|
||
<p>从主干(master)创建一个分支(branch),基于分支去开发功能或修复 Bug;</p>
|
||
</li>
|
||
<li>
|
||
<p>编写实现代码和测试代码(单元测试和集成测试),是不是测试驱动不重要,看个人偏好或团队要求;</p>
|
||
</li>
|
||
<li>
|
||
<p>持续提交代码更新到分支,直到完成;</p>
|
||
</li>
|
||
<li>
|
||
<p>创建 PR(Pull Request,合并请求),邀请其他人帮忙 Review 代码,根据 Review 的结果,可能还需要更新几次;</p>
|
||
</li>
|
||
<li>
|
||
<p>CI 在每一次提交代码到代码库后都会自动运行,运行后主要做这些工作:</p>
|
||
<ul>
|
||
<li>检查代码格式是不是符合规范;</li>
|
||
<li>运行单元测试代码;</li>
|
||
<li>运行集成测试。</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<p>最终这些检查都完成后,CI 会把执行结果显示在 PR 上。通常绿色表示通过,红色表示失败;</p>
|
||
</li>
|
||
<li>
|
||
<p>PR 能合并需要满足两个条件:CI 变绿 + 代码 Review 通过;</p>
|
||
</li>
|
||
<li>
|
||
<p>PR 合并后,CI 会自动构建 Docker Image,将 Image 部署到开发环境;</p>
|
||
</li>
|
||
<li>
|
||
<p>将相应的 Ticket 从看板上的“In Progress”栏移动到“Done”栏。</p>
|
||
</li>
|
||
</ul>
|
||
<p><img src="assets/963f6a02614892e09ef936ac54dc8178.png" alt="img" /></p>
|
||
<p>图片来源:Jira</p>
|
||
<p>正常来讲,你是需要严格遵守开发流程的,但偶尔肯定也有紧急任务,来不及写测试代码,这种情况下,一定要再创建一条 Ticket 跟踪,以确保后续完成测试代码。</p>
|
||
<h3>部署上线流程</h3>
|
||
<p>最早的时候,程序员都是自己管服务器,但是由于这样过于随意,就会导致很多问题出现。</p>
|
||
<p>于是后来有专门的运维团队,将开发好的程序,编译好,数据生成脚本写好,然后写成部署文档,交给运维去手动部署。这个过程无比繁琐、无比慎重,通常几周才部署一次,遇上打补丁才隔几天部署。</p>
|
||
<p>这些年随着容器化、微服务、DevOps 这些技术或概念的兴起,部署已经变得越来越高效,大厂已经开始在部署流程上融合这些理念。</p>
|
||
<p>以前是运维人员按照文档部署,现在已经变成了 DevOps 写自动化部署工具,然后开发人员自己去部署生产环境。</p>
|
||
<p>现在大厂的部署也都实现了自动化,但是流程上还是有一些控制。</p>
|
||
<ul>
|
||
<li>
|
||
<p>首先,部署的不再是程序代码,而是 Docker 的 Image,每次代码合并后 CI 都会自动生成新的 Image,测试也是基于 Image 测试。</p>
|
||
</li>
|
||
<li>
|
||
<p>部署生产环境之前,先在内部的测试环境充分测试。</p>
|
||
</li>
|
||
<li>
|
||
<p>部署生产环境前,需要审批确认,有 Ticket 跟踪。</p>
|
||
</li>
|
||
<li>
|
||
<p>部署时,先部署一部分,监测正常后再全量部署。</p>
|
||
</li>
|
||
<li>
|
||
<p>整个过程都有监控报警,出现问题及时回滚。</p>
|
||
</li>
|
||
</ul>
|
||
<p>如果一切顺利的话,整个生产环境的服务部署过程通常几分钟就完成了,这在以前简直是不敢想象的事。</p>
|
||
<h2>每日站立会议</h2>
|
||
<p>在敏捷开发中,每日站会是非常有名的。在大厂,但凡实施敏捷开发的小组,上班第一件事,就是一起开一个站会,沟通一下项目的基本情况,这也导致会议室越发紧张起来。</p>
|
||
<p>虽然站立会议什么时间开都可以,但是早上无疑是最好的时机,一天工作的开始,开完会全身心去干活。</p>
|
||
<p>**是不是站着开会其实不重要,重点是要高效沟通反馈。**开会时间控制在半小时以内,半小时内不能完成的应该另外组织会议。</p>
|
||
<p>谁来主持站立会议呢?在敏捷的 Scrum 中,有一个角色叫 Scrum Master(敏捷教练、敏捷大师),主要任务就是保证各种敏捷流程的。</p>
|
||
<p>所以通常是由 Scrum Master 主持会议,也可以采用轮班制,每个星期换一名团队成员主持。负责主持会议的人,主要职责是组织会议,一个一个环节开展,控制好会议节奏。</p>
|
||
<p>开会都干什么呢?主要有三个话题:</p>
|
||
<ol>
|
||
<li><strong>成员轮流发言</strong></li>
|
||
</ol>
|
||
<p>每个人轮流介绍一下,昨天干了什么事情,今天计划做什么事情,工作上有没有障碍无法推进。</p>
|
||
<p>一个成员的发言可能是这样的:“昨天我实现了用户登录模块的前端输入框,今天打算完成后端 API 调用,在实现后端的时候需要 API 组的支持,昨天发现他们文档有问题,不知道该找谁。”</p>
|
||
<p>要注意的是,这过程中很容易偏离主题,比如突然有人提了一句:“我们好久没团建了,是不是该出去玩玩了。”很可能大家都很 high 的讨论起来了,这时候会议主持者要及时打断,记录到“问题停车场”,让下一个人继续,先保证大家能高效完成这一环节。</p>
|
||
<blockquote>
|
||
<p>问题停车场(Parking lot question),把需要进一步讨论的问题暂时放到这里,一会儿再讨论。</p>
|
||
</blockquote>
|
||
<p>通过这样的形式,项目成员可以相互了解任务进展,有困难也可以互相支援,及时发现问题和风险。还有一个重要因素,就是每个人对于自己提出的目标,也会信守承诺,努力完成。</p>
|
||
<ol start="2">
|
||
<li><strong>检查最新的 Ticket</strong></li>
|
||
</ol>
|
||
<p>前面提到所有日常工作都是基于 Ticket 来开展的,这些 Ticket 可能是测试报出的 Bug,也可能是产品经理提交的需求,也可能是其他。</p>
|
||
<p>所以每天例会都需要检查一下新增的 Ticket,并且要甄别一下优先级,然后决定是放到当前 Sprint,还是放到 Backlog(任务清单)。</p>
|
||
<p>这个阶段同样要注意不能发散,不要针对 Ticket 的细节展开过多讨论,有需要讨论的同样可以先收集到“问题停车场”,会议组织者需要做好控制。</p>
|
||
<ol start="3">
|
||
<li><strong>停车场问题</strong></li>
|
||
</ol>
|
||
<p>在这个环节,大家可以针对之前来不及讨论的问题进行讨论,能在会议时间内解决的问题,就马上解决,不能解决的会后再私下讨论或者再组织会议。</p>
|
||
<p>当然,大厂的流程规范还有很多,在这里我仅列出与敏捷相关的主要开发流程。</p>
|
||
<h2>总结</h2>
|
||
<p>我们知道,在敏捷开发中有很多概念,像 Backlog、持续交付、每日站会等,这些概念最终要变成实践的话,就必须要通过一定的流程规范来保障这些概念的实施。</p>
|
||
<p>这就是为什么很多公司写代码要求你写自动化测试代码,为什么要用一些像 Jira、禅道这样的项目管理软件来管理任务,为什么要每天开站立会议,为什么要有代码审查。这些都不过是为了保障敏捷的实施。</p>
|
||
<p>如果你在实施敏捷开发的项目工作,就可以多去观察平时工作中这些和敏捷有关的流程规范,再结合敏捷开发中的知识点,就能很好的帮助你理解敏捷开发,理解这些流程规范背后的理论依据。</p>
|
||
<p>如果你工作中不是用的敏捷开发,也可以参考本文中提到的一些实践,尝试着试用起来。</p>
|
||
<p>在下一篇里,我还会以一个具体的项目小组对敏捷的应用为例,继续给你讲讲大厂都在用的那些敏捷方法。</p>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div style="float: left">
|
||
<a href="/专栏/软件工程之美/05 敏捷开发到底是想解决什么问题?.md.html">上一页</a>
|
||
</div>
|
||
<div style="float: right">
|
||
<a href="/专栏/软件工程之美/07 大厂都在用哪些敏捷方法?(下).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":"70997c37ad073cfa","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>
|