原文由知無涯发表于TesterHome社区,点击原文链接可与作者间接交换。
在代码审核的长期实践中,Google总结出了最佳实际,并在此基础上整顿出了这些倡议。整篇文档各局部的连接性并不大,在浏览时,你能够选取本人感兴趣的局部,而不用按程序浏览全文。当然,咱们仍旧倡议你按程序通读全篇,你会发现这份文档对你十分有用。
一. 代码审核的规范
规范
代码审核的目标是为了保障代码库中的代码品质继续改良,代码审核的工具和流程都是为了实现这个目标而设计。
为了达到目标,咱们须要权衡得失。
首先,开发人员必须能在工作上 获得停顿 。如果从没向代码库提交代码,那么代码库就不会改善。同时,如果审核者让开发者在提交代码时变得很艰难,那么开发者不得不破费大量的精力解决审核评论,没有能源在将来的提交中改良代码品质。
另一方面,审核者有责任确保提交者的代码品质。随着工夫的推移,代码库的品质不会升高。这有点辣手,冰冻三尺非一日之寒,代码库品质的升高是随着每次代码提交的渺小升高累积而成的,尤其当团队面临很大的工夫压力时,为了实现工作,他们不得不采取一些长期计划。
另外,代码审核者对他们审核的代码有所有权和责任,他们有任务确保代码库是统一的、可保护的,所有这些内容可参见代码审核过程中要看些什么?(What to Look For In a Code Review)这篇文章。
因而,咱们心愿在代码审核中能遵循这条准则:
个别状况下,如果代码提交者的代码能显著进步代码库的品质,那么审核者就应该批准它,只管它并不完满。
这是代码审核中所有规定的 最高准则。
当然,也有例外。例如,一次提交蕴含了零碎中不应退出的性能,那么审核者就不应批准它,即便它设计得十分完满。
还有一个关键点,那就是世上基本就没有“完满”的代码——只有 更好 的代码。审核者不应该要求代码提交者在每个细节都写得很完满。审核者应该做好批改工夫与批改重要性之间的均衡。无需谋求完满,而应寻求 继续的改良 。假使一个 CL 可能改良零碎的可维护性、可读性,那么它不应该仅仅因为不够完满而提早数天(甚至数周)才批准提交。
咱们应该营造这种气氛:当审核者发现某些事件有更好的计划时,他能够无拘束地提出来。如果这个更好的计划并不是非改不可,能够在正文前加上:“Nit:”,让提交者明确,这段评论只是精益求精,你能够抉择疏忽。
留神:在提交代码时不应显著地 好转 代码品质,惟一的例外是 紧急情况。
领导
代码审核还有一项重要的性能:能让开发者学到新常识,可能是编程语言方面的,也可能是框架方面的,或一些惯例的软件设计准则。作为审核者,如果你认为某些评论有助于开发者学到新常识,那就毫不犹豫地写下来吧。分享常识是进步代码品质的一种形式。记住,如果你的评论是纯学习相干的,与文档中提及的规范关系不大,那就最好在后面加上“Nit”,否则就意味着开发者必须在 CL 中修改这个问题。
准则
以技术因素与数据为准,而非集体爱好。
在代码款式上,听从代码款式指南的权威。任何与款式指南不统一的观点(如空格)都是集体偏好。所有代码都应与其保持一致。如果某项代码款式在文档中并未提及,那就承受作者的款式。
任何波及软件设计的问题,都不应由集体爱好来决定。 它应取决于根本设计准则,以这些准则为根底进行衡量,而不是简略的集体认识。当有多种可行计划时,如果作者能证实(以数据或公认的软件工程原理为根据)这些计划根本差不多,那就承受作者的选项;否则,应由规范的软件设计准则为准。
如果没有可用的规定,那么审核者应该让作者与以后代码库保持一致,至多不会好转代码零碎的品质。
抵触解决
在代码审核中碰到抵触时,首先要做的永远是先尝试让单方(开发者和审核者)在两份文档(开发者指南 和 审核者指南)的根底上达成共识。
当很难达成共识时,审核者与开发者最好开一个面对面的会议(或视频会议),而不要持续通过代码审核评论进行解决。(在开会讨论之后,肯定要在评论中记录探讨的后果,以便让当前浏览的人晓得前因后果。)
如果仍旧无奈解决问题,最好的形式是降级。常见的降级形式比拟多,如让更多的人(如团队的其他人)参加探讨,把团队领导卷进来,咨询代码保护人员的意见,让工程经理来决定。千万不要因为开发者与审核者无奈达成统一而让CL停留在阻塞状态。
二. 代码审核过程中要看些什么?
设计
审核一个 CL 最重要的事件就是思考它的整体设计。CL 中的代码交互是否有意义?这段代码应该放到代码库(codebase)里,还是库(library)里?它能很好地与零碎其余局部集成吗?当初退出这个性能是机会正好吗?
性能
这个 CL 所实现的性能与开发者冀望开发的性能是统一的吗?开发者的用意是否对代码的“用户”有益处?此处提到的“用户”通常蕴含最终用户(应用这些开发进去的性能的用户)和开发者(当前可能会“应用”这些代码的开发者)。
绝大多数状况,咱们冀望开发者在提交 CL 进行审核之前,曾经做过充沛的测试。但作为审核者,在审核代码时仍要思考边界状况、并发问题等等。确保毁灭那些通过浏览代码就能发现的缺点。
作为审核者,你 能够 依据须要亲自验证 CL 的性能,尤其是当这个 CL 的行为影响用户交互时,如UI扭转。仅通过浏览代码,你很难了解有哪些扭转,对用户有哪些影响。对于这种批改,能够让开发者演示这个性能。当然,如果不便把 CL 的代码集成到你的开发环境,你也能够本人亲自尝试。
在代码审核过程中,对性能的思考还蕴含一种重要场景:CL 中蕴含一些“并行计算”,可能会带来死锁或竞争条件。运行代码个别很难发现这类问题,通常须要(开发者和审核者)认真思考,以确保不会引入新的问题。(这也是不要引入并发模型的一个好理由,因为它可能引入死锁或竞争条件,同时也减少了代码审核和代码了解的难度。)
复杂性
是不是 CL 能够不用这么简单?在 CL 的每个档次上查看——哪一行或哪几行是不是太简单了?性能是否太简单了?类(class)是否太简单了?“太简单”的定义是代码阅读者不易疾速了解。 同时意味着当前其余开发者调用或批改它时,很容易引入新的缺点。
另一种类型的简单是适度工程化(也称为适度设计)。开发者在设计代码时太过于在意它的通用性,或在零碎中退出了目前不须要的性能。审核者应该特地警觉适度工程化。激励开发者解决 以后 应该解决的问题,而不是开发者揣测未来 可能 须要解决的问题。未来的问题,等碰到的时候,你能力看到它的理论需要和具体情况,到那时再解决也不迟。
测试
同时要求开发者提供 CL 对应的单元测试、集成测试或端到端的测试。测试代码与开发代码应放到同一个 CL 中,除非碰到紧急情况。
确保 CL 中的测试是正确的、理智的、有用的。测试代码并不是用来测试其本身,咱们很少为测试代码写测试代码——这就要求咱们确保测试代码是正确的。
当代码出问题时,是否测试会运行失败?如果代码扭转了,是否会产生误报?是否每个测试都应用了简略有用的断言?不同的测试形式是否做了适合的拆分?
谨记:测试代码也是须要保护的代码。不要因为不会编译打包到最终的产品中,就承受简单的测试代码。
命名
开发者是否为所有的元素(如类、变量、办法等)选取了一个好名称。一个好名称应该足够长,足以明确地形容它是什么,他能做什么,然而也不要长到难以浏览。
正文
开发者是否应用英文写了清晰的正文?是否所有的正文都是必须的?通常当正文解释为什么这些代码应该存在时,它才是必须的,而不是解释这些代码做什么。如果代码逻辑不清晰,让人看不懂,那么应该重写,让它变得更简略。当然,也有例外(例如,正则表达式和简单的算法通常须要正文来阐明),但大部分正文应该提供代码自身没有提供的信息,如这么做背地的起因是什么。
有时候也应该看一下这个 CL 相干的历史正文。例如,以前写的TODO,当初能够删掉了;某段代码批改了,其正文也应随之批改。
留神,正文与类、模块、性能的 文档 是不同的,这类文档应该形容代码的性能,怎么被调用,以及被调用时它的行为是什么。
代码款式
在Google,咱们所有的次要编程语言都要遵循代码款式指南,确保 CL 恪守代码款式指南中的倡议。
如果发现某些款式在代码款式指南中并未提及,在正文中加上“Nit”,让开发者晓得,这是一个小瑕疵,他能够依照你的倡议去做,但这不是必须的。不要因为集体的款式偏好而导致 CL 提早提交。
作者在提交 CL 时,代码中不应蕴含较大的款式扭转。因为这样很难比拟出 CL 中有哪些代码批改,其后的代码合并、回滚会变得更艰难,容易产生问题。如果作者想从新格式化文件,应该把代码格式化作为独自的 CL 先提交,之后再提交蕴含性能的 CL。
文档
如果 CL 批改了编译、测试、交互、公布的形式,那么应查看下相干的文档是否也更新了,如 README 文件、g3doc 页面,或其余所有生成的参考文档。如果 CL 删除或弃用(deprecate)了一些代码,思考是否也应删除相应的文档。如果没有这些文档,让开发者( CL 提交者)提供。
每行代码
在审核代码时,仔细检查 每行 代码。某些文件,如数据文件、生成的代码或较大的数据结构,能够一扫而过。然而人写的代码,如类、性能或代码块不能一目十行,咱们不应假如它是正确的。有些代码得尤其小心——这须要你本人衡量——至多你应该确认你 了解 这些代码在做什么。
如果代码很难读懂,那就加快审核速度,通知开发者你没读懂代码,让他解释与廓清,之后持续审核。在Google,咱们雇佣都是平凡的工程师,你是其中一员。如果你读不懂代码,很有可能其余工程师也不懂。实际上,这么做也是在帮忙当前的工程师,当他读到这段代码时更容易了解代码。所以,让开发者解释分明。
如果你了解这些代码,然而感觉本人不够资格审核它,确保找到一个够资格的人来审核,尤其是比较复杂的问题,如平安、并发、可拜访性、国际化等等。
上下文
把 CL 放到一个更广的上下文中来看,通常很有用。在审核工具中,咱们往往只能看到开发者批改的那局部代码。更多时候从整个文件的角度来读代码才有意义。例如,有时候你只看到增加了几行代码,但从整个文件来看,你发现这4行代码增加到了一个50行的办法中。在减少之后,须要把它拆分成更小的办法。
把 CL 放到零碎的上下文中来思考也很有用。CL 能晋升零碎的代码健康状况,还是让零碎变得更简单、更难测试?不要承受好转零碎健康状况的代码。大多数零碎变得很简单都是由每个细小的简单累积而成的,在提交每个 CL 时都应防止让代码变得复杂。
好的方面
如果在 CL 中看到一些比拟好的方面,通知开发者,尤其是当你在审核代码时增加了评论,他在回复你的评论,尝试向你解释的时候。审核者往往只关注代码中的谬误,他们也应该对开发者的优良实际示意激励和感激。有时候,通知开发者他们在哪些方面做得很好,比通知他们在哪些方面做得有余更有价值。
三. 代码审核的步骤
第一步:全面理解 CL
浏览 CL 形容,理解CL实现的性能。判断这个批改是否有意义?如果答案是“否”,请立刻回复,并解释为什么要勾销这个批改。当回绝的同时,你最好向开发者给出倡议,这种状况应该怎么做。
例如,你可能会这么说:“这个 CL 的代码看起来挺不错的,谢谢你!不过,咱们正在删除 FooWidget 零碎,并用新的零碎代替他,目前最好不要批改它(或对它退出新的性能)。倡议换种形式,你看重构一下 BarWidget 类,怎么样?”
在下面的例子里,你回绝了这个 CL ,并提供了一种可选计划。整个过程,你都 很有礼貌 。这种礼貌十分重要,这在通知对方:只管不批准你的观点,但我很尊重你。
如果这种状况(开发者提交了你认为不应该这么做的 CL)经常出现,那么你应该考虑一下,是不是应该优化团队的开发流程或内部贡献者(针对某些与内部开发人员独特合作的场景)的公布流程,与开发者先进行充沛的沟通确保他曾经了解开发的内容,再进行开发。最好在开发者开发一大堆工作之前就说“不”,以防止大量不必要的返工。
第二步:查看 CL 的主体局部
找到蕴含 CL “主体”局部的文件。通常,如果一个文件蕴含大量的逻辑批改,那么它就是 CL 的主体局部。先扫视这些主体局部有助于为其余局部理出上下文。如果 CL 太大,很难找到主体局部的地位,能够咨询开发者的倡议,你应该先看哪些局部,并倡议他把一个 CL 拆分成多个。
如果发现 CL 中有一些重要的设计缺点或设计问题,立刻给出反馈,即便当初还没来得及审核其余局部。实际上,审核其余局部很有可能是浪费时间。只有这个设计问题足够大,在从新设计时,其余代码很有可能会隐没或变得无关紧要了。
为什么要立刻对这重要设计问题给出反馈呢?有两个起因:
开发者常常在收回 CL 之后就立刻基于这个 CL 开始新的工作。如果你发现正在审核的 CL 有重要设计问题,那么他正在做的新 CL 还得返工。咱们应该及时指出,防止开发者在基于谬误的设计下做了太多工作。
重要设计谬误比小批改破费更多的工夫。每个开发者在进行开发工作时都有最初期限;为了在保障代码品质的前提下按时交付,开发者须要尽快从新设计 CL。
第三步:以适合的程序扫视 CL 的其余局部
在确认 CL 没有重要设计问题之后,整顿出扫视文件的程序,并确保不会脱漏任何文件。通常,在扫视了次要文件之后,最简略的形式就是依照代码审核工具出现进去的程序遍历每个文件。有时候,先浏览测试代码更有帮忙,因为看了测试代码之后,你就明确这个 CL 的冀望行为是什么。
四. 代码审核的速度
为什么应该尽快审核代码?
在Google,咱们优化了团队开发产品的的速度,而不是优化单个开发人员写代码的速度。单个开发人员的开发速度诚然重要,但远没有整个团队的开发速度 重要 。
当代码审核者很慢的时候,会产生以下几件事:
作为整体,团队的进度升高了。 是的,单个审核者没有对代码审核及时响应,而是在实现其余的工作。如果每个人都这样的话,团队的新性能开发或缺点修复就会延期,累积下来,延期可能是几天、几周,甚至几个月,团队中每个人都在期待他人审核(或再次审核)本人的代码。
开发者开始抗议代码审核流程。 如果审核者几天反馈一次,每次都要求 CL 重大扭转,这样开发者就会变得很丧气,很困惑。通常,这表白为对审核者太过“严格”的埋怨。如果审核者 同样 要求大量的批改(确实有利于改善代码品质的批改),并且每当开发者更新后,审核者 迅速 响应,埋怨就会隐没。大多数对于代码审核流程的埋怨实际上能够通过让流程变得更快来解决。
影响代码品质。 当审核很慢时,会减少开发者的压力,他会认为本人的代码不尽人意。缓慢的审核也会妨碍代码清理、重构,妨碍已有 CL 的进一步改善。
代码审核应该多迅速?
如果你当初没有进行一项须要集中精力的工作,你应该在收到审核邀请时,短时间内就开始代码审核。
在收到审核申请时,一个工作日是审核响应的最长期限 ,即第二天早上做的第一件事件。
遵循这些规定意味着一个典型的 CL 的几轮审核(如果有必要的话)都会在一天内实现。
速度 vs. 中断
当然也有列外。如果你正在做一项须要集中精力的工作时,例如写代码时,不要打断本人。 钻研显示,在被打断之后,开发者很长时间能力进入打断前的状态。所以,与其在写代码的时候打断本人,不如让另外一位开发者稍候。否则 老本更高 。
什么时候审核呢?在下一个工作断点之后再审核。这个断点可能是你以后的代码曾经实现的时候、午饭后、某个会议完结、从公司的餐厅回来,等等。
疾速响应
当咱们谈及代码审核的速度时,咱们指的是 响应工夫 ,而不是审核 CL 整个流程走下来直到提交代码所破费的工夫。整个流程也应该快,但 更重要的是集体的疾速响应,而不是整个流程快点实现。
即便整个 审核 流程走下来会破费很多工夫,但在整个过程中,审核者都在疾速响应,这也会很大水平加重开发者对 “慢” 代码审核的挫败感。
当收到一个 CL 审核的时候,如果你过后太忙没有工夫做残缺的审核,你依然能够疾速响应,告知开发者你稍后会做审核(然而过后没工夫),他能够让他其余能疾速响应的人先审核,或者先提供一些初步的反馈。(留神:这并不意味着你能够打断以后的编码工作,还是应该在工作的断点后再审核。)
有一点很重要,当审核者反馈“LGTM”时,意味着他花了足够的工夫审核代码,并且认为代码满足代码规范。 然而,每个人还是应该迅速响应。
跨时区的审核
当有时差时,尽量在开发者来到办公室之前给他反馈。如果对方曾经上班回家,尽量确保在他在第二天早上来公司之前给出反馈。
LGTM的评论
为了放慢代码审核,有一些确定的场景,你应该给出 LGTM/同意 的反馈,即便开发者仍有一些未解决的反馈(unresolved comments)。这些场景如下(满足任一场景即可):
审核者置信开发者会对所有未解决的反馈做出适合的响应。
未解决的反馈无关紧要,开发者 没必要 解决。
审核者应该说明他做出的 LGTM 是哪种场景。
LGTM 特地值得思考,尤其是当开发者与审核者跨时区时,否则开发者又得等一整天工夫能力失去“LGTM,赞成”。
大 CL
如果某位开发者提交一份很大的代码审核,你不大确认本人是否有工夫审核它,一种典型的响应是让开发者把一个CL拆分成多个,而不是让审核者一次审核大 CL。这样对审核者比拟有用,尽管开发者有些额定的工作要做,这是值得的。
如果一个 CL 不能 拆分成多个,并且你很难在短时间内审核代码,至多在CL的整体设计上向开发者提出反馈,以便让开发者改良。作为一个审核者,你的指标之一是:尽量不要阻塞开发者,让他能迅速采取下一步口头。当然,前提是不会升高代码品质。
代码审核在一直改善中
在遵循本文中的倡议进行代码审核之后,只管代码审核很严格,你会发现,在运行一段时间后,整个流程会越来越快。开发者学会了衰弱的代码须要什么,在发送 CL 之前会尽量保障代码品质,因而须要审核的工夫会越来越短。审核者学会了疾速响应,不会在审核中减少不必要的延时。
然而, 不要为了设想中的速度晋升,在代码审核规范或品质上斗争 ——实际上,从长期来,这样做并不会节省时间。
紧急情况
当然,也会有紧急情况,要求审核流程尽快实现,此时代码品质也有适当的弹性空间。然而,请先确保它确实属于紧急情况。如果不确认,先查看一下什么是紧急情况,这篇文章具体讲述了哪些状况属于紧急情况,哪些不是。
五. 怎么写代码审核的评论?
放弃友善。
解释起因。
给出明确的信息,指出问题所在,让开发者最初做决定。
激励开发者简化代码,给代码增加正文,而不是向你解释为什么这么简单。
礼貌评论
在审核代码时,礼貌和尊重都很重要,与此同时,评论应该形容清晰,有利于开发者改良代码。确保你对代码的评论应该是针对“代码”,而不是针对“开发者”自己。当然,不用总是遵循这条准则,然而当你要说某些可能让人丧气或引起争议的话时,肯定要对事不对人。例如:
不好的说法:“为什么你在这儿应用线程?显著这儿应用并发没有任何益处。”
好的说法:“这儿的并发模型减少了零碎的复杂度,我在性能上没有看到益处。因为没有性能晋升,这段代码最好还是由多线程改成单线程。”
解释为什么
从下面“好的说法”中,咱们看到,它有助于开发者了解 为什么 你要写这条评论。当然,不用每次都解释为什么,但某些情景——说明你的用意、你正在遵循的最佳实际、你在晋升代码衰弱水平——解释起因是有必要的。
提供领导
一般而言,修复 CL 的责任人是开发者,而不是审核者。 你不用为开发者写出具体的设计方案,也不用为他写代码。
但这不代表审核者能够不提供任何帮忙。作为审核者,你应该在指出问题所在与提供间接领导之间做好均衡。指出问题,并让开发者本人做决定,这样有助于开发者自我学习,审核者本人也很省工夫。这种形式,开发者也更容易找出更好的解决方案,因为绝对审核者,开发者对本人的代码更相熟。
当然,有些时候也能够给出间接的批示、倡议或代码。毕竟,代码审核的首要指标是尽可能让 CL 变得更好;主要指标才是晋升开发者的技能,以缩短审核工夫。
承受解释
如果某段代码你看不懂,让开发者解释,通常后果是他们会重写代码让它更清晰。有时候,增加正文也能够,只有它不是用来解释一段过于简单的代码。
仅仅把解释写到代码审核工具里不利于当前的代码阅读者。 只有几种状况能够这样,如当你正在审核一段你并不是很相熟的代码时,开发者向你解释的文字,其余开发者都晓得,那这种解释就不用写到代码里。
六. 代码评论被回绝时,应如何解决?
有时候,面对审核者的评论,开发者可能会回绝。他们不批准你的倡议,或者他们认为你太过于刻薄了。
谁是对的?
当开发者不批准你的倡议时,先思考他们是否正确。开发者往往更相熟代码,在某些方面他们对代码有更好的见解。他们的论点是否理由充沛?站在代码衰弱的角度,是否应该如此?如果答复“是”,通知他们,他们是对的,能够疏忽这条评论。
但开发者并非总是对的。此时,代码审核者应该进一步解释为何他置信本人的倡议是正确的。一个很好地解释通常蕴含两局部:对开发人员的回复的解释和进一步阐明这么改的必要性。
尤其当审核者置信他们的倡议可能无效晋升代码品质时,他们应该持续保持这种批改。即便有证据显示这种代码晋升须要一些额定的工作,也值得做。晋升代码品质往往是聚沙成塔的过程。
有时候,须要通过好几轮的解释与廓清,你的倡议才会被承受。确保从头至尾都放弃礼貌 ,让开发者晓得你在 听 他谈话,只是不 批准 他的观点而已。
焦躁的开发者
有时候,审核者置信,如果本人保持改良,那么开发者可能会心烦。这种事确实偶有产生,但通常持续时间很短,稍后他们会感激审核者,正是他的保持让代码品质得以晋升。通常,如果在评论中放弃礼貌,开发者基本就不会焦躁,审核者不用担心。焦躁大多是因为写评论的形式没有把焦点放在代码品质上。
稍后再清理
一种常见的回绝起因是:开发者想尽快实现它(这种心理很常见)。他们不想再进行一轮审核,只想快点把 CL 提交到代码库。所以,他们说他们会在稍后的 CL 中再清理代码,这样你应该对 以后 CL 评论 LGTM了。有些开发者确实是这么做的,他们随后确实创立了一个新的 CL,用于修复当问题。然而,教训通知咱们,从开发者提交了原始 CL 之后,工夫过得越久,他们清理代码的可能性越小。除非开发者在以后 CL 之后 立刻 清理代码,否则当前也不会清理。这并不是说开发者不靠谱,而是因为他们有太多的开发工作要做,迫于其余工作的压力曾经忘了清理之前提交的代码这件事。因而,咱们倡议,最好保持让开发者 当初 就开始清理代码,而不是提交到代码库 之后 。咱们应该有种理念, 代码进化的常见起因有几个,“稍后再清理” 便是其中之一。
如果 CL 让代码变得复杂,那么它必须在提交之前清理结束,除非是紧急情况。如果 CL 处处是问题却不立刻解决,开发者应该给本人发一个清理代码相干的 bug,把它调配给本人,以防止忘记。除此之外,在代码中加上 TODO 正文和相干的 bug 编号。
与严格相干的常见埋怨
如果以前你在做代码评审时比拟宽松,当初忽然变得严格起来,有些开发者可能会埋怨。没关系,通过晋升审核代码的速度通常会让埋怨隐没。
有时候,让埋怨隐没的过程比拟漫长,长达数月,但最初,开发者会往往趋于同意严格审核代码的价值,因为在严格审核的帮忙下,他们写出了平凡的代码。一旦大家意识到这种严格带来的价值,甚至最强烈的抗议者可能会变成最弱小的拥护者。
抵触解决
作为审核者,如果你曾经遵循本文中提到的所有规定,但仍旧与开发者之间产生了难以解决的抵触,能够参考代码审核的规范 ,以其中提到的规定与实践来领导抵触解决。
原文由知無涯发表于TesterHome社区,点击原文链接可与作者间接交换。
今日份的常识已摄入~
想理解更多前沿测试开发技术:欢送关注「第十届MTSC大会·上海」>>>
1个主会场+12大专场,大咖星散精英齐聚