需求评审、敏捷开发流程、改善代码风格消灭隐藏bug、理解HTTPS
说到敏捷开发,相信大家都多少有了解。
目前大部分互联网公司的开发模式肯定不是传统的瀑布式开发,更多的应该是偏向于敏捷开发。
最近一段时间参与的项目,项目组采用的是敏捷开发迭代制度,虽然可能和严格意义上的敏捷开发有所区别,但是适合的才是最好的。
在实践中,通过项目的反思总结,制定适合自己团队的敏捷模式是最好的。
首先简单来介绍一下敏捷开发:
敏捷开发以用户的需求进化为核心,采用迭代、循序渐进的方法进行软件开发。
在敏捷开发中,软件项目在构建初期被切分成多个子项目,各个子项目的成果都经过测试,具备可视、可集成和可运行使用的特征。
换言之,就是把一个大项目分为多个相互联系,但也可独立运行的小项目,并分别完成,在此过程中软件一直处于可使用状态——来自百度百科。
简单来讲,在快速发展的互联网时代,开发周期不宜太长,采取小步迭代,更能适应当今这个时代。
网上的敏捷开发的流程图是这样的:
我们项目组的流程,实际是这样的:
虽然和许多标准的敏捷流程不完全一样,但其实我们的流程保留了核心的几个环节。
接下来聊聊每个环节的主要任务及内容:
一、需求整理阶段
时间:
此环节的时间往往不计入迭代周期,一方面是需求和设计经常会发生变动,而功能设计确定后,进入开发阶段的变动比较少;另一方面是开发在进入当前迭代的时候,此时产品就应该进入到下一个迭代的设计周期了。
参与人员:
产品人员、技术负责人、项目经理
工作任务:
此环节任务其实不少,包括了产品人员对迭代的需求进行梳理,对需求完成设计。产品在完成产品方案后,小范围召集产品经理、技术负责人、进行评审。在交互稿、设计稿都完成后,进行召开迭代会议。
产品关注:
此阶段产品的主要工作是在需求池中进行筛选,整理出高优先级的任务,作为此次迭代的功能列表。因笔者都是在小公司,所以产品文档、交互稿都是由产品人员通过原型来展现。
踩过的坑:
某次该会议叫了过多的人,导致会议时间过长,会议效果也不好(由于此环节主要是对总体功能列表进行讨论,只需叫上产品小队、技术负责人就够了)。
还有就是:设计的原型在此阶段就做的太细,导致部分需求其实并不需要(此环节设计以大的框架及流程为主,细节的交互、规则等可以在之后再根据需要进行完善)
二、迭代会议阶段
时间:
此环节为迭代周期正式开始
参与人员:
项目组所有人员
工作任务:
此阶段为任务讲解、计划。主要为产品经理对此次迭代的主要任务进行讲解,包括需求来源、产品设计,技术人员对此次迭代的时间进行排期。
产品关注:
该会议就是传说中的评审会议。
产品要多做准备工作,因为会有很多人来怼你,主要还是以业务流程、规则、交互等具体实现的东西,因为技术要进行开发,很多东西需要问清楚。
踩过的坑:
这个环节坑就是看被怼的惨不惨?? 。
主要有两方面的准备:
一个是人,因为前期有准备过小范围的评审(需求整理会议),你要拉拢其他的人员认可你的东西,这样在会议中,这部分人会帮你回答(至少不会提问你)。
另一方面还是文档(原型),在设计的时候要多思考,把各种情况考虑全,这样在会议中被提问时,就能够很好的回答他人。
三、迭代计划阶段
时间:
开发、测试阶段
参与人员:
项目组所有人员
工作任务:
此阶段为开发阶段,技术负责人对功能列表进行拆分、排期,开发人员开始进入编码阶段。
测试人员根据需求书写测试用例,在开发完成提成后,进行产品测试。
产品关注:
本阶段产品主要是和开发人员保持沟通,一些在开发过程中会发现的有疑问的地方,需要产品去决策,该如何做。
产品提测后,产品人员也要及时去对开发好的功能进行验证,看是否符合预期。
在这间隙,产品需要开始对下一个迭代的需求进行梳理。
每日例会:
此会议初衷是对每天的工作进行回顾,主要是看当天的任务完成情况,是否有难处等,让大家对项目的进度有一个了解。但是实际应用中,我们的例会效果不是很好。
建议通过一些协作工具,用图表来展示,这样更有直观性。
踩过的坑:
迭代过程中最大的坑应该是需求变更了。原则上迭代会结束后,不能在此迭代里新增需求。但是需求变更常常就会有新增,这时候需要去评估。
如果是改动大的需求,需要召集小分队人员进行讨论,看是否必要加入此次需求;如果是小的改动,则进行相关文档的更新,并通知到项目组成员。
四、发布演示阶段
时间:
开发测试完成后,进行产品的发布更新
参与人员:
项目组所有人员
工作任务:
此阶段为迭代的尾声,在测试完成后,根据需要在不同的环境进行发布,发布后,可能需要去演示。
产品关注:
到此阶段,就正式完成一个迭代的周期了。产品人员应该也梳理好了下一个迭代的需求,在本迭代发布且通过后,就需要开始新一轮的迭代工作了。
踩过的坑:
更新经常到更凌晨。发布前一定要做好测试、发布前准备工作,要定好发布计划,不然很容易陷入到发布-测试-发现bug-修改bug-测试-发现新的bug….反正每次更新都不省心,经常到凌晨。
正如食物腐烂之前,可能会发出异味。当代码存在隐藏问题时,代码也会表现出一些异状,我们称之为代码异味(code smell),它存在于整体结构和代码设计阶段,暗示代码块或通用的编程模式中可能存在更深层次的问题。
代码异味通常被认为是暗示代码段需要重构的标志,但这并不是说代码有bug或者是无用的 。通常情况下,存在代码异味的代码块也能够运行的很好,但是一般难以维护和扩展,这就会导致一些技术问题,特别是在大型项目中,这种现象更加明显。
那么常见的代码异味有哪些呢?又要如何解决呢?下面我们将重点介绍10种最常见的代码异味以及如何对它们进行“除臭”。
1.紧耦合
存在的问题
紧耦合是指两个对象相互依赖于彼此的数据或函数,如果修改其中一个对象,那么另一个对象也需要修改。当两个对象过于紧耦合时,修改代码可能会是一场噩梦,同时更有可能在每次修改时引入bug。
例如:
在这种情况下,Worker类和Bike类紧密耦合。如果有一天你想开车去上班而不是骑自行车?你必须进入Worker类,将所有与Bike类相关的代码替换为与Car类相关的代码。这就会变得很混乱,很容易出错。
解决方法
你可以通过添加一个抽象层来降低耦合程度。在这种情况下,Worker类不仅可以骑自行车,还可以开车,还可以开卡车,甚至还可以骑摩托车。这些都属于交通工具,不是吗? 所以可以创建一个交通工具接口,根据你的需求,允许插入和修改不同类型的交通工具。
例如:
2.上帝对象
存在的问题
上帝对象是指包含太多变量和函数的大型类或模块。“知道得太多”和“做得太多”都会造成一些问题,原因有以下两点。首先,其他类或模块会变得过分依赖于数据(紧密耦合)。其次,由于所有代码都挤在同一个地方,使得整体结构杂乱无章。
解决方案
取一个上帝对象,然后根据它存在的问题来分离它的数据和函数,再将这些分组转换成对象。相较于上帝对象,分解为许多小对象可能会更好。
例如,假设你有一个巨大的User类:
您可以将其转换为以下内容的组合:
这样在下次需要修改登录过程时,就不必通过巨大的User类,而是通过易于管理的Credentials类!
3.长函数
存在的问题
顾名思义,长函数是指函数太长了。虽然没有一个特定的数字表示多少行代码对于一个函数来说“太长”,但当你看到这个函数时,你就会知道它是不是太长。这几乎是上帝对象问题的一个更严重的版本,一个长函数包含了太多的功能实现。
解决方案
长函数应该被分解成许多子函数,其中每个子函数被设计为处理单个任务或问题。理想情况下,原始的长函数将变成一个子函数调用列表,从而使代码更清晰,更易于阅读。
4.参数过多
存在的问题
函数或类的构造函数拥有太多的参数会造成一些问题,原因有以下两点。首先,这会使得代码不易阅读,测试也更加困难。其次,更重要的是,这意味着该函数的功能太模糊,承担着太多功能的实现。
解决方案
尽管“过多”对于参数列表而言是主观的,但我们建议对任何超过3个参数的函数保持关注并尽量避免。当然,有时候一个函数有5个甚至6个参数也是允许的,前提是有合理的理由。
大多数情况下,不存在一种方法能更好地将该函数分解为两个或更多不同的函数。与“长函数”不同的是,这个问题不能仅仅通过用子函数替换代码来解决 ,因为是函数本身需要分解为单独的子函数,而每个子函数都需要包含各自的功能。
5.命名模糊的标识符
存在的问题
一个或两个字母的变量名、无明显意义的函数名称、过分修饰的类名、使用变量类型标记的变量名称(例如,b_isCounted表示布尔变量),最糟糕的是,在一个代码中混合使用不同的命名规则,所有这些都将导致代码难以阅读,难以理解和难以维护。
解决方案
为变量,函数和类命名是一个难学的技能。如果你正在参与一个已有的项目,请仔细观察现有的标识符命名方式。如果存在命名风格指南,那么请记住它并时刻遵守它。如果是新项目,就可以考虑形成自己的命名风格并且坚持下去。
一般而言,变量名称应该简短但具有描述性。函数名通常应该至少有一个动词,并且函数名称应该表现出该函数的功能,但是不要使用太多的单词,类名也是如此。
6.幻数
存在的问题
当你正在浏览一些其他人写的代码,这时你发现了一些硬编码的数字。它们也许是if语句的一部分,或者是一些难以理解的计算的一部分,看起来没什么意义,而你需要修改该模块,但却无法理解这些数字的含义,这会使你非常苦恼。
解决方案
在编程时,应该不惜一切代价避免这些所谓的“幻数”。硬编码数字在写的时侯是有意义的,但是它们很快就会失去所有含义 ,特别是当其他人试图维护你的代码时。
其中一种解决方法是留下数字的注释,但更好的选择是将幻数转换为常量变量(用于计算)或枚举(用于if语句和switch语句)。通过给幻数起一个名字,代码可读性一目了然,同时也不太容易出现错误。
7.深度嵌套
存在的问题
有两种主要的语句可能造成深度嵌套代码:循环和条件语句。深度嵌套的代码并不总是很糟糕,但可能会产生问题,因为它很难理解(特别是变量没有被很好地命名的情况下),甚至更加难以修改。
解决方案
如果你发现自己正在编写一个双重,三重甚至四重for循环,那么代码将可能试图在超出自身的范围外查找数据。所以你应该提供一种方法,使之可以通过包含该数据的对象或模块函数调用来请求数据。
另一方面,深层嵌套的if语句通常表明你试图在单个函数或类中处理过多的逻辑代码块。事实上,深层嵌套和长函数往往是同时出现的。如果你的代码有大量的switch语句或嵌套的if-then-else语句,你可能需要实现一个状态机或策略模式。
8.未处理异常
存在的问题
异常的功能是非常强大的,但却容易被滥用。不正确地使用throw-catch语句可能会导致调试难度大幅增长。例如,忽略或掩盖捕获的异常。
解决方案
不要忽略或掩盖捕获的异常,而是要打印出异常的及其调用信息,这样调试人员才可以发现错误。如果你的程序悄无声息地运行失败,那么将来你可能就要头痛不已了!此外,我更倾向于输出特殊的异常信息而非所有异常。
9.重复的代码
存在的问题
你在程序多个无关部分执行相同的逻辑代码块,然后发现需要修改该逻辑代码块,但是却不记得所有执行该代码块的地方,假设最终你只修改了5个位置,而实际上有8个位置的代码块需要进行更改,这就会导致结果出现错误。
解决方案
解决重复代码问题的首要选择是转化为函数。假设你正在开发一个聊天应用程序,你是这样编写的:
在代码中的其他地方,你发现你需要执行一个相同的“这个用户在线吗?”检查。这时不要复制粘贴代码块,而是把它放到一个函数中:
这样在代码的任何地方,你都可以使用isUserOnline()函数进行检查。如果你需要修改此逻辑代码块,就只需要修改该方法,再将其应用于所有调用该方法的地方就可以了。
10.缺乏注释
存在的问题
代码在任何地方都没有注释。没有函数的功能注释,没有类的使用概述,没有对算法的解释等等。有人可能会说,写得好的代码不需要注释,但事实上,即使是写的最好的代码也不如注释更容易被理解。
解决方案
易于维护的代码块应该是代码写得足够好以至于不需要注释,但它仍然有注释。在写注释的时候,要记住你的目的是为解释代码块为什么存在,而不是解释代码块在做什么。注释能帮助你更好的理解自己和他人的代码,减少工作量,所以不要忽视他们。
如何编写风格良好的代码
显而易见,大多数不规范的代码都是由于对良好的编程原则和代码风格的忽视。假如你能够严格遵守DRY原则(Don"t repeat yourself)就能消除大部分的重复代码,而掌握单一职责原则就可以避免创造巨大的上帝对象。
千万不要轻视这个问题,如果连你都无法一目了然地看懂自己的代码,更何况其他人呢?
摘要:本文尝试一步步还原HTTPS的设计过程,以理解为什么HTTPS最终会是这副模样。但是这并不代表HTTPS的真实设计过程。在阅读本文时,你可以尝试放下已有的对HTTPS的理解,这样更利于“还原”过程。
我们先不了聊HTTP,HTTPS,我们先从一个聊天软件说起,我们要实现A能发一个hello消息给B:
如果我们要实现这个聊天软件,本文只考虑安全性问题,要实现:
A发给B的hello消息包,即使被中间人拦截到了,也无法得知消息的内容
如何做到真正的安全?
这个问题,很多人马上就想到了各种加密算法,什么对称加密、非对称加密、DES、RSA、XX、噼里啪啦~
而我想说,加密算法只是解决方案,我们首先要做的是理解我们的问题域——什么是安全?
我个人的理解是:
A与B通信的内容,有且只有A和B有能力看到通信的真正内容
好,问题域已经定义好了(现实中当然不止这一种定义)。对于解决方案,很容易就想到了对消息进行加密。
题外话,但是只有这一种方法吗?我看未必,说不定在将来会出现一种物质打破当前世界的通信假设,实现真正意义上的保密。
对于A与B这样的简单通信模型,我们很容易做出选择:
这就是对称加密算法,其中图中的密钥S同时扮演加密和解密的角色。具体细节不是本文范畴。
只要这个密钥S不公开给第三者,同时密钥S足够安全,我们就解决了我们一开始所定问题域了。因为世界上有且只有A与B知道如何加密和解密他们之间的消息。
但是,在WWW环境下,我们的Web服务器的通信模型没有这么简单:
如果服务器端对所有的客户端通信都使用同样的对称加密算法,无异于没有加密。那怎么办呢?即能使用对称加密算法,又不公开密钥?请读者思考21秒钟。??
答案是:Web服务器与每个客户端使用不同的对称加密算法:
如何确定对称加密算法
慢着,另一个问题来了,我们的服务器端怎么告诉客户端该使用哪种对称加密算法?
当然是通过协商。
但是,你协商的过程是没有加密的,还是会被中间人拦截。那我们再对这个协商过程进行对称加密就好了,那你对协商过程加密的加密还是没有加密,怎么办?再加密不就好了……好吧,进行鸡生蛋蛋生鸡的问题了。
如何对协商过程进行加密
新问题来了,如何对协商过程进行加密?密码学领域中,有一种称为“非对称加密”的加密算法,特点是私钥加密后的密文,只要是公钥,都可以解密,但是公钥加密后的密文,只有私钥可以解密。私钥只有一个人有,而公钥可以发给所有的人。
虽然服务器端向A、B……的方向还是不安全的,但是至少A、B向服务器端方向是安全的。
好了,如何协商加密算法的问题,我们解决了:使用非对称加密算法进行对称加密算法协商过程。
这下,你明白为什么HTTPS同时需要对称加密算法和非对称加密算法了吧?
协商什么加密算法
要达到Web服务器针对每个客户端使用不同的对称加密算法,同时,我们也不能让第三者知道这个对称加密算法是什么,怎么办?
使用随机数,就是使用随机数来生成对称加密算法。这样就可以做到服务器和客户端每次交互都是新的加密算法、只有在交互的那一该才确定加密算法。
这下,你明白为什么HTTPS协议握手阶段会有这么多的随机数了吧。
如何得到公钥?
细心的人可能已经注意到了如果使用非对称加密算法,我们的客户端A,B需要一开始就持有公钥,要不没法开展加密行为啊。
这下,我们又遇到新问题了,如何让A、B客户端安全地得到公钥?
我能想到的方案只有这些:
方案1. 服务器端将公钥发送给每一个客户端
方案2. 服务器端将公钥放到一个远程服务器,客户端可以请求得到
我们选择方案1,因为方案2又多了一次请求,还要另外处理公钥的放置问题。
公钥被调包了怎么办?又是一个鸡生蛋蛋生鸡问题?
但是方案1有个问题:如果服务器端发送公钥给客户端时,被中间人调包了,怎么办?
我画了张图方便理解:
显然,让每个客户端的每个浏览器默认保存所有网站的公钥是不现实的。
使用第三方机构的公钥解决鸡生蛋蛋生鸡问题
公钥被调包的问题出现,是因为我们的客户端无法分辨返回公钥的人到底是中间人,还是真的服务器。这其实就是密码学中提的身份验证问题。
如果让你来解决,你怎么解决?如果你了解过HTTPS,会知道使用数字证书来解决。但是你想过证书的本质是什么么?请放下你对HTTPS已有的知识,自己尝试找到解决方案。
我是这样解决的。既然服务器需要将公钥传给客户端,这个过程本身是不安全,那么我们为什么不对这个过程本身再加密一次?可是,你是使用对称加密,还是非对称加密?这下好了,我感觉又进了鸡生蛋蛋生鸡问题了。
问题的难点是如果我们选择直接将公钥传递给客户端的方案,我们始终无法解决公钥传递被中间人调包的问题。
所以,我们不能直接将服务器的公钥传递给客户端,而是第三方机构使用它的私钥对我们的公钥进行加密后,再传给客户端。客户端再使用第三方机构的公钥进行解密。
下图就是我们设计的第一版“数字证书”,证书中只有服务器交给第三方机构的公钥,而且这个公钥被第三方机构的私钥加密了:
如果能解密,就说明这个公钥没有被中间人调包。因为如果中间人使用自己的私钥加密后的东西传给客户端,客户端是无法使用第三方的公钥进行解密的。
话到此,我以为解决问题了。但是现实中HTTPS,还有一个数字签名的概念,我没法理解它的设计理由。
原来,我漏掉了一个场景:第三方机构不可能只给你一家公司制作证书,它也可能会给中间人这样有坏心思的公司发放证书。这样的,中间人就有机会对你的证书进行调包,客户端在这种情况下是无法分辨出是接收的是你的证书,还是中间人的。因为不论中间人,还是你的证书,都能使用第三方机构的公钥进行解密。像下面这样:
第三方机构向多家公司颁发证书的情况:
客户端能解密同一家第三机构颁发的所有证书:
最终导致其它持有同一家第三方机构证书的中间人可以进行调包:
数字签名,解决同一机构颁发的不同证书被篡改问题
要解决这个问题,我们首先要想清楚一个问题,辨别同一机构下不同证书的这个职责,我们应该放在哪?
只能放到客户端了。意思是,客户端在拿到证书后,自己就有能力分辨证书是否被篡改了。如何才能有这个能力呢?
我们从现实中找灵感。比如你是HR,你手上拿到候选人的学历证书,证书上写了持证人,颁发机构,颁发时间等等,同时证书上,还写有一个最重要的:证书编号!我们怎么鉴别这张证书是的真伪呢?只要拿着这个证书编号上相关机构去查,如果证书上的持证人与现实的这个候选人一致,同时证书编号也能对应上,那么就说明这个证书是真实的。
我们的客户端能不能采用这个机制呢?像这样:
可是,这个“第三方机构”到底是在哪呢?是一个远端服务?不可能吧?如果是个远端服务,整个交互都会慢了。所以,这个第三方机构的验证功能只能放在客户端的本地了。
客户端本地怎么验证证书呢?答案是证书本身就已经告诉客户端怎么验证证书的真伪。
也就是证书上写着如何根据证书的内容生成证书编号。客户端拿到证书后根据证书上的方法自己生成一个证书编号,如果生成的证书编号与证书上的证书编号相同,那么说明这个证书是真实的。
同时,为避免证书编号本身又被调包,所以使用第三方的私钥进行加密。
这地方有些抽象,我们来个图帮助理解:
证书的制作如图所示。证书中的“编号生成方法MD5”就是告诉客户端:你使用MD5对证书的内容求值就可以得到一个证书编号。
当客户端拿到证书后,开始对证书中的内容进行验证,如果客户端计算出来的证书编号与证书中的证书编号相同,则验证通过:
但是第三方机构的公钥怎么跑到了客户端的机器中呢?世界上这么多机器。
其实呢,现实中,浏览器和操作系统都会维护一个权威的第三方机构列表(包括它们的公钥)。因为客户端接收到的证书中会写有颁发机构,客户端就根据这个颁发机构的值在本地找相应的公钥。
题外话:如果浏览器和操作系统这道防线被破了,就没办法。想想当年自己装过的非常规XP系统,都害怕。
说到这里,想必大家已经知道上文所说的,证书就是HTTPS中数字证书,证书编号就是数字签名,而第三方机构就是指数字证书签发机构(CA)。
CA如何颁发数字证书给服务器端的?
当我听到这个问题时,我误以为,我们的SERVER需要发网络请求到CA部门的服务器来拿这个证书。?? 到底是我理解能力问题,还是。。
其实,问题应该是CA如何颁发给我们的网站管理员,而我们的管理员又如何将这个数字证书放到我们的服务器上。
我们如何向CA申请呢?每个CA机构都大同小异,我在网上找了一个:
拿到证书后,我们就可以将证书配置到自己的服务器上了。那么如何配置?这是具体细节了,留给大家google了。
也许我们需要整理一下思路
我们通过推算的方式尝试还原HTTPS的设计过程。这样,我们也就明白了为什么HTTPS比HTTP多那么多次的交互,为什么HTTPS的性能会差,以及找到HTTPS的性能优化点。
而上面一大堆工作都是为了让客户端与服务器端安全地协商出一个对称加密算法。这就是HTTPS中的SSL/TLS协议主要干的活。剩下的就是通信时双方使用这个对称加密算法进行加密解密。
以下是一张HTTPS协议的真实交互图(从网上copy的,忘了从哪了,如果侵权麻烦告知):
能不能用一句话总结HTTPS?
答案是不能,因为HTTPS本身实在太复杂。但是我还是尝试使用一段话来总结HTTPS:
HTTPS要使客户端与服务器端的通信过程得到安全保证,必须使用的对称加密算法,但是协商对称加密算法的过程,需要使用非对称加密算法来保证安全,然而直接使用非对称加密的过程本身也不安全,会有中间人篡改公钥的可能性,所以客户端与服务器不直接使用公钥,而是使用数字证书签发机构颁发的证书来保证非对称加密过程本身的安全。这样通过这些机制协商出一个对称加密算法,就此双方使用该算法进行加密解密。从而解决了客户端与服务器端之间的通信安全问题。
好长的一段话。
后记
以上是个人为理解HTTPS而编造出来的自圆其说的看法。顶多只能算是HTTPS的科普文章。如有错误,请指出,万分感谢。
那么,我为什么会觉得以这种方式理解HTTPS会更容易呢?我个人给出的答案是:当你自己为一家人做一次菜时,你就会理解妈妈天天做菜的不易了。
- “抢人大战”后实探北辰楼市 热度隐升刚需需求旺盛
- 设备需求释放 工程机械销量增长 6股享大餐
- 新一线城市满足你所有需求,我的家沈阳 未来有无限发展空间!
- 古代光棍有需求咋办?除了去青楼,朝廷还给出了3种强有力的措施
- 一切从满足玩家需求出发 Supercell独特思路打造移动电竞职业联赛
- 破壁机十大排行 美的破壁机满足人们多种饮食需求
- 买房从实际需求出发不要被“赠送面积”而左右!
- 古代打仗的时候,士兵们是怎么解决生理需求的?一般用这4种方法
- 安居客发布5月国民安居报告 房市稳中有升 购房需求趋于理性
- 国网重庆电力:全力保障市民迎峰度夏用电需求