我曾经和我认为优秀的程序员一起工作,但是他们是真正优秀的程序员吗?是什么让他们如此优秀?(或者,他们只是普通的程序员?)

原文:

1.- DRY: Don’t repeat yourself.

DRY 是一个最简单的法则,也是最容易被理解的。但它也可能是最难被应用的(因为要做到这样,我们需要在泛型设计上做相当的努力,这并不是一件容易的事)。它意味着,当我们在两个或多个地方的时候发现一些相似的代码的时候,我们需要把他们的共性抽象出来形一个唯一的新方法,并且改变现有的地方的代码让他们以一些合适的参数调用这个新的方法。

DRY 这一法则可能是编程届中最通用的法则了,目前为止,应该没有哪个程序员对这一法则存有异议。但是,我们却能发现,一些程序在编写单元测试(unit
testing)时忘记了这一法则:让我们相像一下,当你改变一个类的若干接口,如果你没有使用DRY,那么,那些通过调用一系例类的接口的unit
test的程序,都需要被手动的更改。比如:如果你的unit test的诸多test
cases中没有使用一个标准共有的构造类的方法,而是每个test
case自己去构造类的实例,那么,当类的构造函数被改变时,你需要修改多少个test
cases啊。这就是不使用DRY法则所带来的恶果。

 

最近读到Mark Pearl博客里一篇名为《Programming, a Subset of
Writing》的文章,其中的观点让我十分推崇,尤其是下面这段:

 

2.- 短小的方法.

至少,我们有下面三个不错的理由要求程序员们写下短小的方法。

  1. 代码会变得更容易阅读。
  2. 代码会变得更容易重用(短方法可以减少代码间的耦合程度)
  3. 代码会变得更容易测试。

优秀程序员和普通程序员最大的区别在于,优秀的程序员会使用整洁、易于理解的方式进行编程,任何不必要的复杂代码均不会出现。和我工作过的那些真正优秀的程序员总是尊学这样的编程步骤:写代码、重构、进一步重构。

澳门新葡萄京官网注册 1

3.- 良好的命名规范

使用不错的统一的命名规范可以让你的程序变得更容易阅读和维护,当一个类,一个函数,一个变量的名字达到了那种可以“望文生义”的境界话,我们就可以少一些文档,少一些沟通。文章《编程中的命名设计那点事 》可以给你一些提示。

就像日常生活技能一样,提高自己的办法唯有不断练习,
但是除了重构和进一步重构之外,你还能做些什么?

课程大纲

  • 什么是好代码

  • 如何编写优雅的代码

  • 如何做出优雅的设计

  • 如何规划合理的架构

  • 如何处理遗留代码

4.- 赋予每个类正确的职责

一个类,一个职责,这类规则可以参考一下类的SOLID 澳门新葡萄京官网注册,法则。但我们这里强调的不是一种单一的职责,而是一个正确的职责。如果你有一个类叫Customer,我们就不应该让这个类有sales
的方法,我们只能让这个类有和Customer有最直接关系的方法。

还有一种提升自我的方法,很简单,只需要对编程的其他重要要素保持开放的心态就好了。如单元测试、异常处理甚至交流技巧都非常重要。如果忽略或轻视这些因素也许会导致你过度自信,甚至成为一个自负的人。

什么是好代码

对于代码质量的定义需要于从两个维度分析:主观的,被人类理解的部分;还有客观的,在计算机里运行的状况。

我把代码质量分为五个层次,依次为:

  • 完成功能的代码

  • 高性能的代码

  • 易读的代码

  • 可测试的代码

  • 可扩展的代码

5.- 把代码组织起来

把代码组织起来有两具层次。

  • 物理层组织:无论你使用什么样的目录,包(package)或名字空间(namespace)等的结构,你需要把你的类用一种标准的方法组织起来,这样可以方便查找。这是一种物理性质的代码组织。
  • 逻辑层组织
    所谓逻辑层,主要是说,我们如果把两个不同功能的类或方法通过某种规范联系和组织起来。这里主要关注的是程序模块间的接口。这就是我们经常见到的程序模块的架构。

我认为鼓吹“忽略实际情况,循规蹈矩地遵守软件开发实践就能成为一个优秀程序员”的说法非常荒谬。有时我会认为他们只是些
“自负的程序员”,虽然,老实说,他们中的一些人还是很优秀的。

如何编写可读的代码

在很多跟代码质量有关的书里都强调了一个观点:程序首先是给人看的,其次才是能被机器执行。

6.- 创建大量的单元测试

单元测试是最接近BUG的地方,也是修改BUG成本最低的地方,同样也是决定整个软件质量好坏的成败的地方。所以,只要有可能,你就应该写更多的,更好的单元测试案例,这样当你未来有相应代码改变的时候,你可以很简单知道你代码的改变是否影响了其它单元。

我非常赞同我以前的同事 Russell Politzky 曾经说过的一句话:

逐字翻译

在评价一段代码能不能让人看懂的时候,可以自己把这段代码逐字翻译成中文,试着组成句子,之后把中文句子读给另一个人没有看过这段代码的人听,如果另一个人能听懂,那么这段代码的可读性基本就合格了。

而实际阅读代码时,读者也会一个词一个词的阅读,推断这句话的意思,如果仅靠句子无法理解,那么就需要联系上下文理解这句代码,如果简单的联系上下文也理解不了,可能还要掌握更多其它部分的细节来帮助推断。大部分情况下,理解一句代码在做什么需要联系的上下文越多,意味着代码的质量越差。

逐字翻译的好处是能让作者能轻易的发现那些只有自己知道的、没有体现在代码里的假设和可读性陷阱。无法从字面意义上翻译出原本意思的代码大多都是烂代码,比如“ms代表messageService“,或者“ms.proc()是发消息“,或者“tmp代表当前的文件”。

7.- 经常重构你的代码

软件开发是一种持续的发现的过程,从而让你的代码可以跟上最新的实际需求的变化。所以,我们要经常重构自己的代码来跟上这样的变化。当然,重构是有风险的,并不是所有的重构都是成功的,也不是我们随时都可以重构代码。下面是两个重构代码的先要条件,以避免让你引入更多的BUG,或是把本来就烂的代码变得更烂。

  1. 有大量的单元测试来测试。正如前面所说,重构需要用大量的单元测试来做保障和测试。
  2. 每次重构都不要大,用点点滴滴的小的重构来代替那种大型的重构。有太多的时候,当我们一开始计划重构2000行代码,而在3个小时后,我们就放弃这个计划并把代码恢复到原始的版本。所以,我们推荐的是,重构最好是从点点滴滴积累起来的。

那些自负的程序员往往是教条、狭隘和不切实际的。在我们的业务中,这会导致他们做出不合适的和有缺陷的设计。

遵循约定

约定包括代码和文档如何组织,注释如何编写,编码风格的约定等等,这对于代码未来的维护很重要。

大家刚开始工作时,一般需要与部门的约定保持一致,包括一些强制的规定,如代码的格式化设置文件;或者一些非强制的约定,如工程的命名等。

从更大的范围考虑,整个行业的约定和规则也在不断的更新。所以在工作中也要对行业动向和开源项目保持关注,如果发现部门中哪一项约定已经过时了,那么可以随时提出来,由平台的架构师小组review之后推进平台更新这些规则。

8.- 程序注释是邪恶的

这一条一定是充满争议的,大多数程序员都认为程序注释是非常好的,是的,没错,程序注释在理论上是非常不错的。但是,在实际过程序当中,程序员们写出来的注释却是很糟糕的(程序员的表达能力很有问题),从而导致了程序注释成为了一切邪恶的化身,也导致了我们在阅读程序的时,大多数时候,我们都不读注释而直接读代码。所以,在这里,我们并不是鼓励不写注释,而是——如果你的注释写得不够好的话,那么,你还不如把更重要的时间花在重构一下你的代码,让你的代码更加易读,更加清楚,这比会比注释更好。

如下是一些程序员经常挂在嘴边的话:

文档和注释

对于文档的标准很简单,能找到、能读懂就可以了,一般一个工程至少要包含以下几类文档:

  1. 对于项目的介绍,包括项目功能、作者、目录结构等,读者应该能3分钟内大致理解这个工程是做什么的。

  2. 针对新人的QuickStart,读者按照文档说明应该能在1小时内完成代码构建和简单使用。

  3. 针对使用者的详细说明文档,比如接口定义、参数含义、设计等,读者能通过文档了解这些功能(或接口)的使用方法。

有一部分注释实际是文档,比如javadoc。这样能把源码和注释放在一起,对于读者更清晰,也能简化不少文档的维护的工作。

还有一类注释并不作为文档的一部分,比如函数内部的注释,这类注释的职责是说明一些代码本身无法表达的作者在编码时的思考,比如“为什么这里没有做XX”,或者“这里要注意XX问题”。

一般来说函数内部注释的数量应该不会有很多,也不会完全没有,一般滚动几屏幕看到一两处左右比较正常。过多的话可能意味着代码本身的可读性有问题,而如果一点都没有可能意味着有些隐藏的逻辑没有说明,需要考虑适当的增加一点注释了。

其次也需要考虑注释的质量:在代码可读性合格的基础上,注释应该提供比代码更多的信息。文档和注释并不是越多越好,它们可能会导致维护成本增加。

9.- 注重接口,而不是实现

这是一个最经典的规则了。接口注重的是——“What”是抽象,实现注重的是——“How”是细节。接口相当于一种合同契约,而实际的细节相当于对这种合同契约的一种运作和实现。运作是可以很灵活的,而合同契约则需要是相对需要稳定和不变的。如果,一个接口没有设计好而需要经常性的变化的话,那我们可以试想一下,这代来的后果,这绝对会是一件成本很大的事情。所以,在软件开发和调设中,接口是重中之重,而不是实现。然而我们的程序员总是注重于实现细节,所以他们局部的代码写的非常不错,但软件整体却设计得相对较差。这点需要我们多多注意。

  • 所有的测试必须是单元测试

  • 要不惜代价达到100%的代码测试覆盖率

  • 所有使用mocks/stubs的测试,必须基于mock库进行实现

  • 不管业务目标是什么,所有的应用程序都必须建立在DDD模式之上

  • 所有使用数据库的程序必须使用ORM来操作数据

  • 不使用ORM是非常糟糕的,而且难于维护

  • 代码注释不应该存在于代码中,因为存在注释表明代码不够清楚明了,不能直接表达其含义,应该总是使用代码来表达程序的含义而不是注释

  • 任何一个你产出的文档,比如说设计文档,总是会过时的,用处很少

  • 你唯一需要的文档就是代码

  • 你唯一需要的模型也是代码

  • 自顶向下的设计是不会成功的,这种尝试总会失败,拥护这种设计的人只是没找着门路,最终他们还是会折服于“演进式设计”所带来的优越性,没错,就是TDD

  • 除了面向对象,你用别的编程范式根本写不出好的软件,因为只有OO能降低复杂度

如何编写可发布的代码

刚开始接触高并发线上系统的新同学经常容易出现一个问题:写的代码在发布之后才发现很多考虑不到的地方,比如说测试的时候没问题,项目发布之后发现有很多意料之外的状况;或者出了问题之后不知道从哪下手排查,等等。

这里介绍一下从代码编写完成到发布前需要注意的一些地方。

10.- 代码审查机制

所有人都会出错,一个人出错的概率是很大的,两个人出错的概率就会小一些,人多一些,出错的概率就会越来越小。因为,人多了,就能够从不同的角度看待一个事情,虽然这样可能导致无效率的争论,但比起软件产品release后出现问题的维护成本,这点成本算是相当值得的。所以,这就是我们需要让不同的人来reivew代码,代码审查机制不但是一种发现问题的最有效的机制,同时也是一种可以知识共享的机制。当然,对于Code Review来说,下面有几个基本原则:

  • 审查者的能力一定要大于或等于代码作者的能力,不然,代码审查就成了一种对新手的training。
  • 而且,为了让审查者真正负责起来,而不是在敷衍审查工作,我们需要让审查者对审查过的代码负主要责任,而不是代码的作者。 
  • 另外,好的代码审查应该不是当代码完成的时候,而是在代码编写的过程中,不断地迭代代码审查。好的实践的,无论代码是否完成,代码审核需要几天一次地不断地进行。

文章出处:

你认识这类程序员吗?如果是,你认为他们的水平如何?经验表明这类极端的思维,既不是完全正确也不是完全错误,只是不合逻辑。

处理异常

新手程序员普遍没有处理异常的意识,但代码的实际运行环境中充满了异常:服务器会死机,网络会超时,用户会胡乱操作,不怀好意的人会恶意攻击你的系统。

对一段代码异常处理能力的第一印象应该来自于单元测试的覆盖率。大部分异常难以在开发或者测试环境里复现,依靠测试团队也很难在集成测试环境中模拟所有的异常情况。

而单元测试可以比较简单的模拟各种异常情况,如果一个模块的单元测试覆盖率连50%都不到,很难想象这些代码考虑了异常情况下的处理,即使考虑了,这些异常处理的分支都没有被验证过,怎么指望实际运行环境中出现问题时表现良好呢?

更加切实的考虑和合理的推理才能帮助你成为一名优秀的程序员。磨练技艺,增强技能固然很好,但是当我们在做这些事情的时候,应该从实际情况出发,认真考虑实施其所需的范围、成本、环境等各种因素。将这些合理的因素纳入考虑范畴需要成熟的思考,才能成为优秀的程序员。

处理并发

而是否高质量的实现并发编程的关键并不是是否应用了某种同步策略,而是看代码中是否保护了共享资源:

  • 局部变量之外的内存访问都有并发风险(比如访问对象的属性,访问静态变量等)

  • 访问共享资源也会有并发风险(比如缓存、数据库等)。

  • 被调用方如果不是声明为线程安全的,那么很有可能存在并发问题(比如java的hashmap)。

  • 所有依赖时序的操作,即使每一步操作都是线程安全的,还是存在并发问题(比如先删除一条记录,然后把记录数减一)。

前三种情况能够比较简单的通过代码本身分辨出来,只要简单培养一下自己对于共享资源调用的敏感度就可以了。

但是对于最后一种情况,往往很难简单的通过看代码的方式看出来,甚至出现并发问题的两处调用并不是在同一个程序里(比如两个系统同时读写一个数据库,或者并发的调用了一个程序的不同模块等)。但是,只要是代码里出现了不加锁的,访问共享资源的“先做A,再做B”之类的逻辑,可能就需要提高警惕了。

当然,也需要大量的练习。

优化性能

性能是评价程序员能力的一个重要指标,但要评价程序的性能往往要借助于一些性能测试工具,或者在实际环境中执行才能有结果。

如果仅从代码的角度考虑,有两个评价执行效率的办法:

  • 算法的时间复杂度,时间复杂度高的程序运行效率必然会低。

  • 单步操作耗时,单步耗时高的操作尽量少做,比如访问数据库,访问io等,这里需要建立对各种操作的耗时的概念。

而实际工作中,也会见到一些同学过于热衷优化效率,相对的会带来程序易读性的降低、复杂度提高、或者增加工期等等。所以在优化之前最好多想想这段程序的瓶颈在哪里,为什么会有这个瓶颈,以及优化带来的收益。

再强调一下,无论是优化不足还是优化过度,判断性能指标最好的办法是用数据说话,而不是单纯看代码。

稿源:ThoughtWorks洞见

日志

日志代表了程序在出现问题时排查的难易程度,对于日志的评价标准有三个:

  • 日志是否足够,所有异常、外部调用都需要有日志,而一条调用链路上的入口、出口和路径关键点上也需要有日志。

  • 日志的表达是否清晰,包括是否能读懂,风格是否统一等。这个的评价标准跟代码的可读性一样,不重复了。

  • 日志是否包含了足够的信息,这里包括了调用的上下文、外部的返回值,用于查询的关键字等,便于分析信息。

对于线上系统来说,一般可以通过调整日志级别来控制日志的数量,所以打印日志的代码只要不对阅读造成障碍,基本上都是可以接受的。

如何编写可维护的代码

可维护性要对应的是未来的情况,但是一般新人很难想象现在的一些做法会对未来造成什么影响。

避免重复

代码重复分为两种:模块内重复和模块间重复。两种重复都在一定程度上说明了编码的水平有问题。现代的IDE都提供了检查重复代码的工具,只需点几下鼠标就可以判断了。

除了代码重复之外,还会出现另一类重复:信息重复。

比方说每行代码前面都写一句注释,一段时间之后维护的同学忘了更新注释,导致注释的内容和代码本身变得不一致了。此时过多的注释反而成了鸡肋,看之无用,删之可惜。

随着项目的演进,无用的信息会越积越多,最终甚至让人无法分辨哪些信息是有效的,哪些是无效的。

如果在项目中发现好几个东西都在做同一件事情,比如通过注释描述代码在做什么,或者依靠注释替代版本管理的功能,这些都是需要避免的。

划分模块

模块内高内聚与模块间低耦合是大部分设计遵循的标准,通过合理的模块划分能够把复杂的功能拆分为更易于维护的更小的功能点。

一般来说可以从代码长度上初步评价一个模块划分的是否合理,一个类的长度大于2000行,或者一个函数的长度大于两屏幕都是比较危险的信号。

另一个能够体现模块划分水平的地方是依赖。如果一个模块依赖特别多,甚至出现了循环依赖,那么也可以反映出作者对模块的规划比较差,今后在维护这个工程的时候很有可能出现牵一发而动全身的情况。

有不少工具能提供依赖分析,比如IDEA中提供的Dependencies
Analysis功能,学会这些工具的使用对于评价代码质量会有很大的帮助。

值得一提的是,绝大部分情况下,不恰当的模块划分也会伴随着极低的单元测试覆盖率:复杂模块的单元测试非常难写的,甚至是不可能完成的任务。

简洁与抽象

只要提到代码质量,必然会提到简洁、优雅之类的形容词。简洁这个词实际涵盖了很多东西,代码避免重复是简洁、设计足够抽象是简洁,一切对于提高可维护性的尝试实际都是在试图做减法。

编程经验不足的程序员往往不能意识到简洁的重要性,乐于捣鼓一些复杂的玩意并乐此不疲。但复杂是代码可维护性的天敌,也是程序员能力的一道门槛。

跨过门槛的程序员应该有能力控制逐渐增长的复杂度,总结和抽象出事物的本质,并体现到自己设计和编码中。一个程序的生命周期也是在由简入繁到化繁为简中不断迭代的过程。

简洁与抽象更像是一种思维方式,除了要理解、还需要练习。多看、多想、多交流,很多时候可以简化的东西会大大超出原先的预计。

如何做出优雅的设计

当程序的功能越来越多时,编程就不再只是写代码,而会涉及到模块的划分、和模块之间的交互等内容。对于新同学来说,一开始很难写出优雅的设计。

这一节会讨论一下如何能让自己编写的代码有更强的“设计感”。

参考设计模式

最容易快速上手的提升自己代码设计水平的方式就是参考其他人的设计,这些前人总结的面对常见场景时如何进行模块划分和交互的方式被称作设计模式。

设计模式的分类

  • ### 创建型模式主要用于创建对象。

  • 结构型模式主要用于处理类或对象的组合。

  • 行为型模式主要用于描述对类或对象怎样交互和怎样分配职责。

由于篇幅有限,这里不再展开每一种设计模式的用途。这部分资料和书籍已经比较全了,可以课下学习。

编写单元测试

单元测试是什么

维基百科上的词条说明:

单元测试是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。

所以,我眼中的“合格的”单元测试需要满足几个条件:

  1. 测试的是一个代码单元内部的逻辑,而不是各模块之间的交互。

  2. 无依赖,不需要实际运行环境就可以测试代码。

  3. 运行效率高,可以随时执行。

单元测试的目的

了解了单元测试是什么之后,第二个问题就是:单元测试是用来做什么的?

很多人第一反应是“看看程序有没有问题”。但是,只是使用单元测试来“看看程序有没有问题”的话,效率反而不如把程序运行起来直接查看结果。原因有两个:

  1. 单元测试要写额外的代码,而不写单元测试,直接运行程序也可以测试程序有没有问题。

  2. 即使通过了单元测试,程序在实际运行的时候仍然有可能出问题。

在这里我总结了一下几个比较常见的单元测试的几个典型场景:

  1. 开发前写单元测试,通过测试描述需求,由测试驱动开发。

  2. 在开发过程中及时得到反馈,提前发现问题。

  3. 应用于自动化构建或持续集成流程,对每次代码修改做回归测试。

  4. 作为重构的基础,验证重构是否可靠。

还有最重要的一点:编写单元测试的难度和代码设计的好坏息息相关,单元测试测的三分是代码,七分是设计,能写出单元测试的代码基本上可以说明这段代码的设计是比较合理的。能写出和写不出单元测试之间体现了编程能力上的巨大的鸿沟,无论是什么样的程序员,坚持编写一段时间的单元测试之后,都会明显感受到代码设计能力的巨大提升。

如何编写单元测试

测试代码不像普通的应用程序一样有着很明确的作为“值”的输入和输出。举个例子,假如一个普通的函数要做下面这件事情:

  • 接收一个user对象作为参数

  • 调用dao层的update方法更新用户属性

  • 返回true/false结果

那么,只需要在函数中声明一个参数、做一次调用、返回一个布尔值就可以了。但如果要对这个函数做一个“纯粹的”单元测试,那么它的输入和输出会有很多情况,比如其中一个测试是这样:

  • 假设调用dao层的update方法会返回true。

  • 程序去调用service层的update方法。

  • 验证一下service是不是也返回了true。

而具体的测试内容可以依赖单元测试框架提供的功能来完成。

单元测试框架

运行框架:

  • jUnit

  • TestNG

  • Spock

Mock框架:

  • Mockito

  • EasyMock

  • PowerMock

  • Spock

由于篇幅限制,这里不再展开具体的框架用法了,有兴趣的同学可以自行搜索相关文章。

如何规划合理的架构

很多新同学的规划都是未来成为架构师,要做架构师就免不了设计架构。而在微博平台工作也会经常跟架构打交道,由于后面有独立的架构课程,这里只是简单介绍一下常见的架构模式。

常见的架构模式

分层架构

分层架构是应用很普遍架构模式,它能降低模块之间的耦合,便于测试开发,它也是程序员需要掌握的基础。

典型的分层架构模式如下:

澳门新葡萄京官网注册 2

上图中是一个4层的架构,在现实场景中,层数不是一个定值,而是需要根据业务场景的复杂度决定。使用分层模型中需要注意,一般来说不能跨层、同层或反向调用,否则会让整个层次模型由单一的树形结构变为网状结构,失去了分层的意义。

但随着程序复杂度的逐渐提升,要严格的按照分层模型逐级调用的话,会产生很多无用的空层,它们的作用只是传递请求,这也违背了软件设计尽量简洁的方向。所以实际场景中可以对各个层次规定“开放”或“关闭”属性,对于“开放”的层次,上层可以越过这层,直接访问下层。

对层次定义“开放”或“关闭”可以帮助程序员更好的理解各层次之间的交互,这类约定需要记录在文档中,并且确保团队中的每个人都了解这些约定,否则会得到一个复杂的、难以维护的工程。

事件驱动架构

事件驱动架构能比较好的解耦请求方和处理方,经常被用在写入请求量变化较大,或者是请求方不关心处理逻辑的场景中,它有两种主要的实现方式:

Mediator

在mediator方式中,存在一个中介者角色,它接收写入方的请求,并把事件分配到对应的处理方(图中的channel),每个处理方只需要关心自己的channel,而不需要与写入方直接通信。

澳门新葡萄京官网注册 3

在微博前些年应用比较多的应用服务-队列-消息处理服务可以认为是属于这种模式。

Broker

在broker方式中不存在中介者的角色,取而代之的是消息流在轻量的processor中流转,形成一个消息处理的链路,如图:

澳门新葡萄京官网注册 4

前一段时间开始推广的storm流式处理属于这种模式,对于较长的处理流程,用broker方式可以避免实现Mediator的复杂性,相对的,管理整个流程变得复杂了。

微内核架构(Microkernel)

微内核架构相对于普通架构最主要的区别是多了“内核”的概念,在编写程序时把基础功能和扩展功能分离:内核中不再实现具体功能,而是定义“扩展点”,增加功能时不再修改主逻辑,而是通过“扩展点”挂接到内核中,如图:

澳门新葡萄京官网注册 5

之前介绍的motan
RPC框架应用了这种设计,这让motan可以通过不同的插件支持更多的功能,比如增加传输协议、定义新的服务发现规则等。

微服务架构

近年来微服务架构的概念一直比较火,它可以解决服务逐渐增长之后造成的难以测试及部署、资源浪费等问题,但也带来了服务调度和服务发现层面的复杂度,如图:

澳门新葡萄京官网注册 6

微博底层实际包含了很多业务逻辑,这些业务逻辑被抽象成一个个服务和模块,不同模块之间通过motan
rpc、grpc或http rest
api进行通信,通过docker和之上的调度服务进行调度和部署,最终成为一个完整的系统。

微服务隔离了各服务之间的耦合,能够有效提升开发效率;除此之外,当微博面对巨大的流量峰值时,可以进行更精细的资源调配和更有效率的部署。

单元化架构

传统的分层架构往往会存在一些中心节点,如数据库、缓存等,这些节点往往容易成为性能瓶颈,并且存在扩容比较复杂的问题。

在面临对扩展性和性能有极端要求的场景时,可以考虑使用单元化架构:对数据进行切分,并将每一部分数据及相关的逻辑部署在同一个节点中,如图:

澳门新葡萄京官网注册 7

在单元化架构中,每个“单元”都可以独立部署,单元中包括独立的计算和存储模块;计算模块只与单元内的存储模块交互,不再需要分库分表等逻辑;而数据与存储更近也降低了网络消耗,进而提高了效率。

微博平台通过对群发服务的单元化改造,实现了百万级每秒的私信推送服务。

如何处理遗留代码

对于一个不断发展的系统,必然有一些遗留下来的历史问题。当遇到了遗留下来的烂代码时,除了理解和修改代码,更重要的是如何让代码朝着好的方向发展,而不是放任不管。

重构是处理遗留代码的比较重要的手段,这一节主要讨论一下重构的相关话题。

何时重构

新同学往往对重构抱有恐惧感,认为重构应该找一个比较长的时间专门去做。这种愿望很好,但是现实中一般很难找出一段相对稳定的时间。

另一方面,重构是比较考验编程水平的一项工作。代码写的不好的人,即使做了重构也只是把不好的代码变了个形式。要达到比较高的水平往往需要不断的练习,几个月做一次重构很难得到锻炼,重构效果也会打折。

所以,重构最好是能够作为一项日常工作,在开发时对刚写完的代码做重构往往单位时间的收益是最大的。

如何重构

一般来说,重构可以抽象成四个方面:

理解现状

如果对当前程序的理解是错的,那么重构之后的正确性也就无从谈起。所以在重构之前需要理解待重构的代码做了什么,这个过程中可以伴随一些小的、基本无风险的重构,例如重命名变量、提取内部方法等,以帮助我们理解程序。

理解目标

在理解了程序做了什么事情之后,第二个需要做的事情就是需要提前想好重构之后的代码是什么样的。

改变代码结构比较复杂,并且往往伴随着风险和不可控的问题。所以在实际动手之前,需要从更高的层次考虑重构之后的模块如何划分,交互是如何控制等等,在这个过程中实际与写代码要做的事情是一致的。

划分范围

烂代码往往模块的划分有一些问题,在重构时牵一发而动全身,改的越多问题越多,导致重构过程不可控。所以在动手重构前需要想办法减少重构改动的范围,一般来说可以只改动相邻层次的几个类,并且只改动一个功能相关的代码,不求一次性全部改完。

为了能够划分范围,在重构时需要采用一些方法解除掉依赖链。比如增加适配器等等,这些类可能只是临时的,在完整的重构完成之后就可以删除掉,看起来是增加了工作量,但是换来的是更可控的影响范围。

确保正确

为了能保证重构的正确性,需要一些测试来验证重构是否安全。最有效的是单元测试,它能提供比集成测试更高的覆盖率,也能验证重构之后的代码设计是否是合理的。

在做一次重构之前需要整理模块的单元测试。遗留代码有可能测试不全,并且难以编写单元测试,此时可以适当的牺牲待重构代码的优雅性,比如提高私有方法的可见性,满足测试的需求。在重构的过程中,这部分代码会被逐渐替换掉。

总结

今天跟大家讨论了一下关于编程的各个方面,关于编程的话题看似很基础,想要做好却并不容易。新同学比较容易急于求成,往往过多的关注架构或者某些新技术,却忽视了基本功的修炼,而在后续的工作过程中,基本功不扎实的人做事往往会事倍功半,难以有更一步的发展。

勿在浮沙筑高台,与各位共勉。

新兵训练营简介

微博平台新兵训练营活动是微博平台内部组织的针对新入职同学的团队融入培训课程,目标是团队融入,包括人的融入,氛围融入,技术融入。当前已经进行4期活动,很多学员迅速成长为平台技术骨干。

具体课程包括《环境与工具》《分布式缓存介绍》《海量数据存储基础》《平台RPC框架介绍》《平台Web框架》《编写优雅代码》《一次服务上线》《Feed架构介绍》《unread架构介绍》

微博平台是非常注重团队成员融入与成长的团队,在这里有人帮你融入,有人和你一起成长,也欢迎小伙伴们加入微博平台,欢迎私信咨询。

讲师简介

秦迪,@蛋疼的AXB 微博平台及大数据部技术专家

个人介绍: