写点什么

烧水泡茶与项目的组织

2020 年 2 月 11 日

烧水泡茶与项目的组织

在旧石器的采集时期,人类以微弱之躯把凶猛的野兽变成嘴里的食儿,凭借的是通过语言沟通进行有效组织的团队的力量。在军事中,组织的好坏是战争胜负的决定因素之一。历史上,无坚不摧的马其顿方阵是军事组织方面的一个经典的案例。16 X 16 的方阵配以萨里沙长矛,方阵较弱的两翼部署重装骑兵。作战时,方阵正面象砧板一样挤压对方阵地,骑兵则侧翼包抄锤击。方阵为砧,骑兵为锤,是为锤砧战术。高加米拉之战,依靠马其顿方阵的优秀组织,亚历山大大帝仅以四五万之众,数百人的损失,击溃了波斯大流士二十万(号称百万)大军。


本文的目的不是去探讨组织行为学的大学问,而是在软件研发的实践中,分享一些组织团队协同工作的个人经验。具体地讲,锁定一个软件项目,项目本身可大可小。当然,说大其实也大不到哪儿去,我们也不大有机会组织几百人上千人完成一个需时一年乃至于几年的大项目。尽管我们公司做的是交换机的嵌入式系统软件,实话实说,也不是从头至尾做一个完整的网络操作系统。所以,多数情况下,不过是基于已有的产品、系统或应用,或增加一项功能,或修改一个缺陷,或解决一个问题。谈到软件项目的组织,从工程学(Engineering)的角度讲,就是把不同角色的工程师聚拢在一起,各就各位,各司其职,朝着共同的目标,以最佳的方式完成项目的所有任务。所谓“最佳”,一方面指的是工作内容的多,工作效率的快和工作质量的好;一方面指的是人力和资源的省。一言以蔽之,多快好省。而瞎指挥和乱组织白白浪费了大家的时间和精力。


组织得好,诸事有序,忙而不乱。大家心情愉悦,士气高涨。每个人都从自己最擅长的方向,最大限度地对项目舒舒服服地贡献自己的力量,有多大劲儿就能够使多大力。组织得不好,诸事千头万绪,剪不断,理还乱。在不断的受挫中,彷徨伴随着恐慌。尽管每个人都很想帮忙,却无从下手,无所适从。


论及组织的能力和才具,我想到了《红楼梦》第十三回王熙凤协理宁国府。说的是贾珍的儿媳贾蓉之妻秦可卿死了,王熙凤帮忙料理丧事。大家知道,凤姐是一个有能力,有办法,有手段且聪明不过的漂亮女人。王熙凤很快地从纷纷嚷嚷的杂务中理出头绪,把丧事办的有条不紊。她的组织其实不过是凭借主子的权威,监督和执行一种良好的秩序,从而让事情有序地顺利进行。


软件研发固有的复杂性决定了软件项目的不确定性,组织起来可不像办丧事那样简单。比如说,丧事中几乎所有的礼仪活动都有章可循:诵经、吊唁和发引,什么时候做什么事,是固定的,不变的。只要行礼如仪,不出差错就万事大吉了。软件项目虽然也讲流程,比如需求分析、功能设计、详细设计、编码实现、功能测试、性能测试、回归测试和发布交付等等。可是,基本上不可能遵循某个一层不变的流程,按照预期做完一个项目。


比如,不妨来看看软件项目的计划这个环节。给定一个需求,预估完成的日期,就不是一件容易的事儿。有时候,我们根据以往的经验,比如曾经做过的类似功能,给出“六到八周”这样一个大致的时间表。不幸的是,“六到八周”是一个太长的周期,长的有点儿不大靠谱。这很像天气预报,假如报告一小时之后有雨,十有八九,基本上靠得住。但是,如果说,半个月后的某日下雪,那可就是太胡扯了。飓风起于青萍之末,南美洲的蝴蝶扇动一下翅膀,就可能引起太平洋的巨浪。不确定的因素太多,长期的天气预报,以目前的技术水平不可能做到准确无误。想当然尔的“六到八周”,随口一说,仿佛一粒特效止痛药,迅速缓解了当时被追问的压力。到头来,治标不治本,欠下的迟早总是要还,一旦面对惨淡尴尬的结局,轻率和孟浪的轻诺最终要付出代价。


另外,更重要的区别是,软件工程师与宁国府的“奴才”岂可同日而语?!有人不听话,迟到了,杀鸡儆猴,王熙凤便拿出主子的款儿命人拉出,打二十板子,以戒下次。在软件组织里面,虽然有管理阶层的分别和规章制度的约束,根本不可能存在“打板子”的可能,最糟糕结局大不了是被迫走人或用脚投票。团队精神很大程度上建立在个人信任的基础上,没有信任作为纽带,项目组织和团队合作便无从谈起。而信任绝对是稀世的奢侈品,拼的是靠谱的(Predictable)个人魅力,包括人品,学识、远见、能力、才具、相貌等等。得之很难,失之甚易。


软件项目的不确定性更体现在质量和进度上。古代冶金和现代冶金给我们做了一个很好的类比:古代冶金充满了神秘的东西,产品的品质全部依赖于匠人的智慧、经验、技巧、乃至于于不可琢磨且不可多得的好运气。成功的产品仿佛是艺术的创造,难以复制。比如,即使干将莫邪重生,也未见得能够再次铸出传说中可以弑王之头的那样锋利的宝剑。而现代冶金技术的成熟在于固定的可操作的工艺流程。只要按照事先定义好的工艺流程,生产的进度是可以预期的。产品质量有定量的指标来检测,合格与否,一目了然。因此,生产的进度和产品质量都是可控的,可复制的,可自动化的,没有任何神秘主义。如果软件的生产可以有象现代冶金那样成熟的工艺流程,问题就简单了,程序员的工作完全可以被自动化的机器取代。这一直是软件工程学努力的方向,幸也?非也?一直都没有取得真正的成功。程序员很大程度上还是象匠人一样工作,所以,高德纳(Donald E. Knuth)的巨著叫做《计算机编程的艺术》(The Art of Computer Programming)。所谓艺术者,简单地讲,对于同样一个功能,两个程序员实现的代码绝没有完全相同的可能。而程序的质量是靠测试的案例来保证的,这些测试案例也不是可测量的定量指标。


良好的组织是克服软件的复杂性和由此带来的不确定性的根本办法,而有效的组织必须建立在团队对项目的一致理解上,这一点无论怎样强调都不过分。项目的理解是需要时间和精力的,在项目计划时就要充分地考虑到这一点。


理解从项目需求开始。因为需求的理解不到位,导致项目返工,失败以至于推倒从来的现象屡见不鲜。理解需求并非易事,因为用户并不总是知道想要什么,常常只有一些模糊的,朦胧的想法。有时候,看到实际的产品时,正切中要害,还是风马牛不相及,才恍然大悟。和用户反复的沟通交流,发现而不是发明用户的需求是产品经理最重要的职责。在一些敏捷开发(Agile Development)或精益创业(Lean Startup)的方法论中,主张用快速迭代的产品原型来验证用户的需求,若不论成本的考量,当然是一种有效的手段。


从设计和实现的角度看,理解很大程度上是对项目的分解,即把一个大的项目细分为一个人一到三天,最多不超过五天可以完成的小任务。请记住,正如上文所说,时间越长,工作量的估计越不靠谱。这些小的任务必须是紧致的,独立的,自包含的,并且能够覆盖整个项目从设计,架构、实现、测试、直至发布交互的全部环节。分解的过程也是理解的过程:干过的人都知道,分解即是科学,有章可循;也是艺术,更需要经验的积累。毫不夸张地讲,分解是软件项目实施的最关键和最困难的步骤,成败全系于此。分解完成后,才谈得上科学的组织活动:把这些小任务分派给不同的工程师,检查他们之间的依赖关系,寻找实施路线图的关键路径,防范潜在的风险。据此,制定出的开发计划才是切实可行的,不是随意拍拍脑袋想当然的结果。


在项目不断向前推进的过程中,麻烦和风险在于由于不一致的理解导致的混淆,糟糕的局面是:老的混淆引起新的混淆,新的混淆叠加老的混淆,混淆套混淆,乱的象一堆麦秸。这种混淆一旦象癌细胞一样扩散到整个团队,项目也就完蛋了。对于人的身体,癌细胞总是会产生,重要的是,健康人的免疫系统能够把他们清除;同样地,对于正在进行的项目,混淆总是会发生,关键在于,团队是否有能力消除混淆。消除混淆的同时,也加深了对项目的理解。首先,要知道哪些是清楚的,哪些是不清楚的;哪些是已知的,哪些是未知的;哪些是确定的,哪些是不确定的。这样,就可以把混淆限制在一定的范围内。然后,再来讨论,可以采取哪些行动把不清楚变清楚,把未知变已知,把不确定变确定。


分歧和混淆相伴而生,这是很正常的事儿。分歧并不可怕,可怕的是真理越辩越糊涂,鸡同鸭讲,大家完全不在一个频道上。以专业为导向,放下激动的情绪和固有的执见,站在数据和逻辑的基础上,进行有效的沟通,使用共同语言或术语,逐步厘清分歧。这是成熟团队的表现。正如英国首相丘吉尔在《二战回忆录》中所述:“我不能说在我们内部从来没有发生过一件分歧,但我和英国参谋长委员会之间滋长了一种谅解,那就是:我们要彼此说服,而不是压服。其所以能够做到这一点,当然是得助于我们讲的是同一的术语,拥有一大套共同的军事理论和战争经验。”以理服人,心服口服;以力服人,口服心不服,或口不服心不服。抛开行政等级的权威,基于事实的分析、归纳和推理而得到的结论才是经得起推敲的决定。


有效的沟通不仅可以化解分歧,还让团队成员之间取长补短,相互启发,避免不必要的疏失。三个臭皮匠顶个诸葛亮,一加一大于二,这正是团队协作的力量。极限编程在克坚攻难的同时,面对共同的压力,心灵上相互依存,多了一丝慰藉。所以,不能小看这种力量。甚至局外人也有可能从不同的角度给出局内人所见不到的灵感和启发。Joel Spolsky 在他的一篇博客The Joel Test: 12 Steps to Better Code中提到一种可用性测试的场景,即所谓的“Hallway Usability Testing”:请求路过你座位的人帮忙使用新写的代码,5 个人就可以帮助发现 95%的隐藏的问题。该文提到了 Jakob Nielsen 的文章Why You Only Need to Test with 5 Users, 对这一现象进行了系统的研究,证明这种方法惠而不费。


我们曾经遇到这样的情况。从需求到设计和编码,花了很长的时间,实现了一个大功能。代码终于完成了,单元测试结束后,交给 QA 团队开始功能集成测试。这时候,麻烦来了。测试不断发现严重的问题,以至于挣扎了很长时间,才勉强完成测试计划。更糟糕的是,此恨绵绵无绝期,问题不测没有,一测就有。没完没了地改代码,看不到尽头。项目无法结束,发布的日期一而再,再而三地向后延宕。收拾残局必须要澄清混淆,以明确具体的行动。在确定项目要求的子功能全部添加后,基于优先级和严重程度,缺陷类选(Bug Triage Meeting)帮助我们对已有的 BUG 分类,清除那些无关紧要的甚至和项目完全没有关系的 BUG,给出并管理一个影响产品发布的关键 BUG 的列表(MUST-FIX List)。于是,所有开发和测试工程师的工作全部集中在这个列表上。在修改和验证这些 BUG 的同时,加强代码的审核(Code Review),避免引入新的 BUG。这样,随着 MUST-FIX 列表的缩小,完成项目便指日可待了。这里面,MUST-FIX 列表帮助团队澄清了项目当前的主要问题和工作。


反躬自省,为什么会出现这样的混淆?大多数貌似技术的问题归根结底都是人为造成的。丰田生产方式(Toyota Production System)的创始人大野耐一(Taiichi Ohno)所反复倡导的五问法(Five Whys)可以系统地帮助我们找到问题的根本原因。就以这个例子而言,虚构的后续剧情也许会按照 Five Whys 的方式这样展开:


  • 为什么出现这样的混淆?回归测试总是发现新的问题和缺陷。

  • 为什么回归测试总是发现新的问题和缺陷?修改问题时加入的新代码破坏了原有的功能。

  • 为什么新代码破坏了原有的功能?


  1. 代码结构混乱,难以理解。

  2. 没有对新代码进行仔细的审核。

  3. 单元测试做的不够。


  • 为什么


  1. 代码结构混乱?没有重构代码的时间。

  2. 没有对新代码进行仔细的审核?没有审核的时间。

  3. 单元测试做的不够?没有单元测试的时间。


  • 为什么没有时间?计划项目时忽略了这些工作量。


糊涂和马虎是测试的天敌,我们必须清楚的认识到,质量是由测试案例和测试计划定义和保证的。有人把进度和质量看做一对矛盾,如果要加快赶进度,就只能牺牲项目的质量。认为匆匆忙忙赶出来的代码,不可能有好的质量。听起来似乎很有道理,其实,这完全是不了解质量概念的糊涂念头。质量是产品和服务的适用性。虽然没有定量的指标很容易地测度程序的质量,但评估程序质量还是有一把尺子,那就是测试案例(Test Cases)。当然,测试案例并不是很完美的尺子,它本身的质量大大地受制于测试工程师的素质。然而,尺子毕竟还是尺子,在实际的项目中,我们只能信任这把尺子。因此,项目再怎么赶进度,只要测试案例能够覆盖代码的功能和性能要求,通过测试的代码的质量就可以认为是没有问题的。因此,进度和质量本来没有必然的关系,并不是说项目做的快了,质量就一定会出问题。


理解项目的范围(Scope)同样重要。无论是开发还是测试,项目中的任何工作和任务都必须有一个界限,明白界限在哪里,才知道如何结束。孙悟空一个筋斗十万八千里,可是,还是跳不出如来佛的手掌心。法力无边的佛祖的手掌可以延展到无穷远,地老天荒,要多远就有多远,无边无沿。孙悟空的筋斗云即使象光一样快,也难以完成这样一个没有定义边界的项目。界限就是责任,刘慈欣《三体》中的一个桥段生动地说明了事情的界限的重要性:


史强护送面壁者逻辑去联合国总部。询问下属任务怎样交接,得知“他们没说”,立刻火冒三丈,怒斥下属道:“你他妈的犯混啊,这么重要的事儿都没落实!”职责所关,的确马虎不得。并非他没有担当的勇气:“跟一辈子都行,但到那边肯定是有交接的,责任分段儿必须明确!这得有条线,咔!之前出事儿责任在我们,之后责任就在他们了。”


计划常常赶不上变化,用户可能没完没了地提出新的要求。像滚雪球一样,项目越做越大,目标越来越模糊。加班加点地玩儿命赶进度,交付还是遥遥无期。解决的办法很简单,就是“分段儿”。项目分成若干阶段,高优先级的任务放在第一阶段,其他的部分放在后续的阶段。因此,中美旷日持久的经贸谈判边打边谈,跌宕起伏,证明这是一个艰难且复杂的过程。收获目前取得的部分成果,分阶段签署经贸协定,从项目组织的角度看,是正确和明智的做法。


毫无疑问,书面文档是帮助我们梳理思路、去除混淆、加深理解、分清责任和追溯检查的必要手段。软件项目的计划、需求分析、功能设计、详细设计和测试计划都必须有相应的文档跟踪记录。战争和国事常常以项目的方式组织,因此,丘吉尔在《二战回忆录》中指出:“几乎我所有的工作都留有记录,我所发出的一切命令,我所拟议的一切调查报告,我所起草的一切电文等等均有记录可查。”另外,丘吉尔致伊斯梅将军、帝国总参谋长、和爱德华布里奇斯爵士的备忘录中又强调:“我发出的一切指示概用书面,或在事后立即用书面的形式加以证实,在国防问题上,一切被认为是有我决定的事,除有书面记录的以外,我概不负责,希望你们清楚地了解这一点。”好记性不如烂笔头,文档在项目中的作用是不可替代的。否则,没有文档就谈不到良好的组织。只是一味地跟着感觉走,说走咱就走,想往哪儿走就往哪儿走,走到哪儿算哪儿。没有任何悬念,这种流浪者的浪漫很快就会演变成灾难,除非你是在做一个小孩子过家家那样幼稚的项目。


总而言之,我认为,读懂和理解项目中的每个环节,包括需求、设计、编码和测试,是项目组织的头等大事;然而,在世俗者的眼里,任务的安排和监管才是重头戏。前者偏重于技术,是工程师的工作范畴;后者偏重于管理,是有流程癖的人们的想头。值得警惕的是,装腔作势的管理过多地干预技术,不仅令人厌恶,更可能导致严重的后果,外行领导内行,混淆由此而生,而团队也会不可挽救地滑向官僚主义。


谈到任务的安排,记得上小学时,读到华罗庚的一篇经典课文《统筹方法》,文章写的很浅显,道理讲的很透彻。让我们重温一下文中提到的烧水泡茶这个例子,看看怎样安排工序才最有效率,不窝工。前提条件是“开水没有;水壶要洗,茶壶茶杯要洗;火生了,茶叶也有了。”可选的办法如下:


  • “办法甲:洗好水壶,灌上凉水,放在火上;在等待水开的时间里,洗茶壶、洗茶杯、拿茶叶;等水开了,泡茶喝。”

  • “办法乙:先做好一些准备工作,洗水壶,洗茶壶茶杯,拿茶叶;一切就绪,灌水烧水;坐待水开了泡茶喝。”

  • “办法丙:洗净水壶,灌上凉水,放在火上,坐待水开;水开了之后,急急忙忙找茶叶,洗茶壶茶杯,泡茶喝。”


一目了然,办法甲最好,用时最少。这其实是从技术的角度组织工序或任务的方法论,用到了数据结构和算法中的有向图(Directed Graph)和拓扑排序(Topological Sorting):根据任务之间的依赖关系,寻找完成所有任务的路线图的关键路径(Critical Path)。如上文所说,我们基本上不会遇到“几百几千,甚至有好几万个任务”的大项目。大多数情况下,一个项目包括的任务并不比烧水泡茶的工序更多。即使某个项目被细分成更多的任务,用一个甘特图(Gantt Chart)就可以轻而易举地把全部任务的计划安排标识得清清楚楚,而生成甘特图的工具遍地都是。我不曾遇到这样的项目,由于“关系多了,千头万绪”而出现“万事俱备,只欠东风”的情况;也没有“临事而迷”导致项目失败的案例。不过是凡事小心谨慎些罢了。所以,一般地,关于任务的安排,我不认为是什么了不起的大学问,真的没必要“小题大做”。然而,误解和混淆导致项目的拖延和失败却是司空见惯。总而言之,流程和管理的环节越简单,效率越高,大家都全神贯注在代码上,哪里还有闲工夫无事生非呢?!回归事物的本质,更多的精力应该放在设计、编码、测试这些和技术与生产直接相关的工作上。


我们都有体会,做一个软件项目,至始至终都在变来变去,需求会变,设计会变,实现当然也得跟着变。并不存在以不变应万变的灵丹妙药,这就是为什么必须保持项目组织的灵活性。作为起步的公司,对用户需求和应用的变化,我们的很大优势在于有能力做出快速敏捷的反应。这当然得益于小公司项目组织的弹性。比如,用户新的需求或问题很快就从支持团队和销售团队传递到工程师团队,我们从不同的开发和测试部门抽取相关的工程师拼凑起一支临时的团队,项目完成后,临时团队就随之解散了。在一个团队里,如果不是在解决问题,就是在制造问题。所以,成功不必在我,尽可能地缩小这种临时团队的规模。只有真正帮得上忙,才被拉进临时团队。任务完成了,马上退出。甚至,有的人都不知道自己被拉进一个临时团队,服务于一个临时项目。但团队中的每个人都清楚地知道自己在做什么,仿佛上紧了的发条,处在全负荷的忙碌的状态。事实证明,这样的临时团队具有强悍的战斗力,效率奇高,尤其对于紧急的项目,常常是攻坚克难的杀手锏。


领导项目的人不能只是发布指令和分派任务。正如上文所说,必须有能力帮助团队理解项目,澄清遇到的混淆。因此,为了保证项目的顺利实施,最好的办法是由最懂的人来领衔。另外,领导项目的人也是项目的负责的人(Owner),要担当起自己的责任。所谓负责的人,实质上是有一个就好。否则,人人都想插手,而需要做出决策时,大家都怕担责,靠边闪,还谈什么负责?!一个项目无论怎样精细的划分,都有可能存在覆盖不到的地方。这些三不管的琐细的事物需要项目的负责人特别小心在意,避免因小失大。项目遭受挫折时,无论多么痛苦,领导者都务必首当其冲,扛起失败的责任。文过饰非没有任何意义,甩锅给他人更是可耻的行为。因此,面对战争的胜负,丘吉尔说:“只有我们在国内的人才能够权衡世界大事的轻重缓急,而最后责任则应由我们承担。”对于前线的指战员,“我一向遵循这样一个原则:对于军事指挥员不应该从效果方面,而应该从努力工作的质量方面加以判断。”


最后,容我讲一个实际的例子来结束本文。我们在开发一个新的版本时,常常有同事抱怨性能的问题,感觉系统运行变慢了。但是不能仅靠感觉就说系统变慢了,必须要有具体的测试案例证明系统变慢了。比如针对某个同样的操作,系统响应时间比以前版本如何。因此,没有必要把开发工程师匆忙地拉进来,凭着臆测来解决这个也许是莫须有的问题。并且,实际上我们也不知道该让哪个开发工程师来看这个问题。因此,问题还得留在测试工程师这边,他们需要给出一个证明系统变慢了的具体案例,作为开发工程师开始工作的契入点。案例找到了,重启系统的某个应用,用时竟是原来版本的两倍。接下来,问题转到了和这一应用相关的开发工程师那里。专业的方法和工具是攻克性能问题的关键,但没有具体的案例,再好的方法和工具都是毫无用处的摆设。利用工具生成测试案例过程的 CPU Profiling,最后发现,某些系统调用耗时比原来多了 2 至 3 倍。问题的根本原因竟是在内核(Kernel)。仔细比较两个版本的内核,性能的下降原来是因为引入了修改漏洞 meltdown 的补丁程序(Patch Code)造成的。需要强调的是,正是因为找到了一个具体的案例,我们才能够有序地组织不同的测试和开发工程师先后介入,得以很快地定位并解决问题。


成竹在胸,才能举重若轻,治大国如烹小鲜,谈笑间神州大定。若把做项目比作下厨,那么,心下明白,化繁为简,象是煮一壶清茶;心下糊涂,缠夹不清,却是活得一盆好糨糊!


作者简介

贾彦民,目前就职于 PICA8。本科与研究生就读于重庆大学,2007 年获中国科学院软件研究所计算机软件与理论博士学位。爱读书,喜欢爬山与摄影(历史、文学、数学)。


2020 年 2 月 11 日 09:001606

评论

发布
暂无评论
发现更多内容

技术都是相通的

小黄鱼

极客大学架构师训练营

了解HashMap数据结构,超详细!

程序员的时光

面试 hashmap HashMap底层原理

iOS性能优化 — 二、卡顿监控及处理

iOSer

性能优化 编程语言 监控 ios开发 卡顿

1024!奈学教育致敬程序员3+2战略发布会重磅来袭

古月木易

程序员 奈学教育

架构师训练营第一周作业

爱码士

架构设计

发布3个月获得5K Star的Luckysheet - 基于MIT协议的开源电子表格

奇异石榴果

Java 前端 Excel 开源项目 html/css

想自己写框架?不会写Java注解可不行

Java架构师迁哥

极客时间架构师训练营第一周学习总结

爱码士

课程总结

LeetCode题解:50. Pow(x, n),暴力法,JavaScript,详细注释

Lee Chen

算法 LeetCode 前端进阶训练营

Java高并发编程的一本百科全书《Java高并发编程详解:多线程与架构设计》,把Java语言中最为晦涩的知识点都详解出来了!

Java架构之路

Java 程序员 架构 并发编程 编程语言

分布式文件存储数据库 MongoDB

哈喽沃德先生

数据库 nosql mongodb mongo 非关系型数据库

1024!奈学教育致敬程序员3+2战略发布会重磅来袭

奈学教育

1024 奈学教育

勾魂!在Github白嫖左程云1470页数据结构与算法+视频

996小迁

Java 架构 面试 面试数据结构与算法

MyBatis-技术专题-动态SQL

李浩宇/Alex

技术体系的构成

异想的芦苇

技术 技术管理 研发体系

两个程序员老友的会面

Philips

敏捷开发

架构师训练营 1 期 - 第五周 - 技术选型

三板斧

极客大学架构师训练营

普通人如何站在时代风口学好AI?这是我看过最好的答案

华为云开发者社区

AI 算法

为什么说容器的崛起预示着云原生时代到来?

华为云开发者社区

容器 云原生 容器云

【API进阶之路】研发需求突增3倍,测试团队集体闹离职

华为云开发者社区

软件开发 开发 开发测试

作为一名Java程序员,技术栈的广度深度都不够还想要高薪?请先把这些技术掌握再说。

Java架构之路

Java 程序员 架构 面试 编程语言

在线EXCEL编辑器-Luckysheet

奇异石榴果

Java 开源 Excel bigtable js

项目吐槽之需求分析一

Geek_XOXO

项目管理 pmp

项目吐槽之需求分析二

Geek_XOXO

项目管理 pmp 项目实战

2020,国产数据库崭露峥嵘的发轫之年

墨天轮

数据库 阿里云 华为云 SQL优化 活动专区

第五周学习代码技术选型总结

三板斧

极客大学架构师训练营

网易:Flink + Iceberg 数据湖探索与实践

Apache Flink

flink 数据湖

大数据上手实战!训练营“9营齐开”第二季限时免费报名啦

Apache Flink

大数据

程序员喜欢的 5 款最佳最牛代码比较工具

程序员生活志

编程 工具

MyBatis-技术专题-拦截器介绍

李浩宇/Alex

不会java的人能不能读《Head First设计模式》?

Nydia

演讲经验交流会|ArchSummit 上海站

演讲经验交流会|ArchSummit 上海站

烧水泡茶与项目的组织-InfoQ