程序员|IT 外企那点儿事(5/9):让我们自身像系统一样升级

程序员|IT 外企那点儿事(5/9):让我们自身像系统一样升级

文章图片

程序员|IT 外企那点儿事(5/9):让我们自身像系统一样升级

文章图片

程序员|IT 外企那点儿事(5/9):让我们自身像系统一样升级

文章图片

程序员|IT 外企那点儿事(5/9):让我们自身像系统一样升级

文章图片

程序员|IT 外企那点儿事(5/9):让我们自身像系统一样升级

文章图片

程序员|IT 外企那点儿事(5/9):让我们自身像系统一样升级

文章图片

程序员|IT 外企那点儿事(5/9):让我们自身像系统一样升级

文章图片

程序员|IT 外企那点儿事(5/9):让我们自身像系统一样升级

文章图片

程序员|IT 外企那点儿事(5/9):让我们自身像系统一样升级

文章图片


外企 , 一个听起来似乎充满光环的名字 , 每年众多大学毕业生向往的地方 。 说起外企 , 总能让人联想到很多令人心动的名词:高薪 , 人性化 , 浮动工作制 , 年假 , 完善的流程 , 各种福利如:旅游 , 室内乒乓球台 , 健身房 , 按摩椅 , 小食品 , 酸奶……然而真正进入了外企 , 时间长了 , 也就发现 , 其实外企也就那么回事!

进行完入职培训 , 便开启了你在外企中的程序人生了 , 需要说明的是 , 此文章不仅限外企 。 如果待足够长的时间 , 你将从程序员 , 高级程序员 , team lead , 一直到 manager , 甚至 director 。 我们姑且宏观审视一下此过程 , 然后再品味一个个细节 。

然而审视的过程猛然发现 , 所谓程序员就是把自己作为程序的人 。 《道德经》第四十二章:道生一 , 一生二 , 二生三 , 三生万物 。 此句大概说明的是宇宙万物发展变化的过程 , 而道则为宇宙万物运行的规律 。

万事万物都在自身的规律 , 万有引力是规律 , 相对论是规律 , 而天天陪伴在我们程序员身边的算法 , 操作系统 , 计算机组成等 , 也可以看成大自然众多规律中的一小部分 , 也只有掌握好这些规律 , 我们才能掌控好计算机的运行 。 系统的开发 , 程序员的升级又何尝不是经历了这样一个过程呢?
一、认识规律: 道
做一个系统 , 首先要掌握此项目所需要的技术 , 如果相关技术没有使用过 , 则此项技术就是一门尚未认知的规律 。 在项目开始之前 , 必须要系统性的认知相关的技术 , 否则面临较大的风险 。 做一个程序员 , 首先要掌握计算机方面的知识 , 对知识的掌握 , 同样需要系统性 , 否则职业生涯也会面临很大的困难 。

系统性在此阶段至关重要 。 如果在项目中 , 对相关的技术没有系统性的认识 , 则会造成以下后果:
设计出的系统不具有扩展性 , 应用了笨拙的方式设计程序 , 出现 Bug时无所适从

不知道大家是否参加过这样的项目开发过程 , 由于时间紧任务重 , 项目组在没有一个人系统了解某项技术的时候就进行了开发 , 大家只好从网上下载一些 Sample code 来通过复制粘贴来拼凑程序 , 甚至连每一项配置或每一行代码都没搞清楚 , 就照猫画虎的拿过来用了 , 这样不但到了后期 , 系统几乎没有任何扩展性 , 并且任何不同于 Sample code 的灵活的改动都是一件十分痛苦的事情 , 项目组就像眉头苍蝇一样四处乱改乱闯 , 但并不清楚每一次改动的真正后果 , 这样要进行大量的尝试和返工 , 最后整的整个项目组很累 , 还没有效果 , 这个过程我称之为“盲试” , 也即在不明白原理的情况下靠反复的体力劳动 , 逐一遍历所有自己认为可能的修改 。

“盲试”是初入职场的程序员经常犯的错误 , 初入职场 , 信心百倍 , 情绪高涨 , 急于出成果是多数时候的心态 , 当一个任务下达到手中的时候 , 并不是系统的阅读文档 , 进行方案评估以及框架设计(这些其实都是磨刀不误砍柴工的事情) , 而是急着上手来做 , 可能在项目的早期 , 能够很快的出效果 , 但是随着项目的进行 , 维护成本越来越大 , 经常加班 , 而效果甚微 。 而对有经验的程序员来讲 , 前期进行了良好的设计 , 后期添加模块 , 需求的灵活变动是相对轻松的事情 。

其实也可以理解这种状况的出现 , 毕竟老板都是心狠手辣的 , 才不会给你那么多事件做调研 ,程序员总是有一种被皮鞭赶着走的感觉 , 从而根本无法系统性的掌握技术和框架设计 。 这也是面试了很多程序员 , 每每都号称做过 ABC 项目 , 分别应用了abc 的技术 , 然而往深入问的时候发现 , 他们对技术 abc 的了解也就仅限于 ABC 项目中 , 对其他一无所知了 。

没有系统性的认识技术 , 则可能写出很多笨拙的程序 , 丑陋的实现 。 因为你只知其一 , 不知其二 , 只知其然 , 不知其所以然 , 本来人家框架中有高效的现成的技术实现这一方面的功能 , 你不知道 , 于是根据自己了解的片面技术勉强拼凑成功能 , 自然也实现了效果 , 然而当自己开始看这方面的经典书籍的时候 , 不禁感慨: “咳 , 原来能够很简单搞定的 , 当时竟然笨笨的写 N 多的代码 。”没有系统性的认知技术 , 出现 Bug 的时候比添加新模块更痛苦 , 因为不明白原理 , 所以只能从表面现象去猜 , 然后又是进行“盲试”的过程 。

因而对技术的系统性认识 , 实在是不但对项目负责 , 更是对自己负责的一件事情 。 如果老板是技术型的 , 在估计项目时间的时候 , 应该劝说其将这方面考虑进去 , 如果老板是非技术型的 , 则程序员也应该自己留下缓冲时间 。 不然你辛辛苦苦白天八小时给老板了 , 晚上再加班几个小时又给老板了 , 你自己如何进步呢?

如果对于程序员 , 对计算机方面的技术没有系统性的认识 , 同样存在上述的问题 。
你的职业生涯同样没有扩展性 。 如果不能够系统的掌握算法 , 数据结构 , 操作系统 , 计算机网络 , 计算机组成等基础知识 , 在程序员的初期可能不明显 , 随便培训培训也能写出不错的程序 , 然而当转换方向或者平台的时候 , 会面临很大的痛苦 。 而我们能够看到的身边的优秀程序员们 , 无论让他们做 CC++还是 Java , 无论是 linux 还是 windows , 他们都能够很快的上手 , 是因为基础好的缘故 。

项目和程序员认识规律的方式 , 其实也无非读书 , 文档 , 及原型开发(对于程序员来说 , 实习阶段相当于 Prototyping) 。

总结:项目或程序员的第一阶段:悟道 , 关键词: “系统性”
二、道生一
当掌握了项目相关的道 , 也即技术的时候 , 就要真正的进入项目开发了 。 当前的项目 , 仍然由一个进程组成的系统比较少了 , 由于数据量的增大 , 基本都会开发多节点的分布式系统 , 然而再复杂的系统 , 也基本是从单节点系统开始做的 , 也即所谓道生一的过程 。 当掌握了计算机相关技术的时候 , 你就可以成为一个真正的程序员了 。 当然不可能让你一开始就管理一个项目组 , 此时唯一要管理好的 , 是你自己 。

开放性和扎实性是此阶段的重中之重 。 对于项目来讲 , 一个好的单节点系统 , 所谓开放 , 就是即便设计单节点的系统 , 也要站在设计多节点的系统的角度来考虑 , 使做出来的系统更加容易被扩展成多节点的系统 。 所谓扎实 , 就是单节点系统要麻雀虽小 , 五脏俱全 , 扎扎实实的实现大部分功能 , 并有相当量的测试用例来保证功能的正确 。

否则会造成以下后果:
当做多节点系统时候 , 发现单节点系统需要大量修改 , 甚至等于白做 , 重新开始 。 单节点不稳定 , 以至于多节点时 Bug 丛生 , 但不知道是因为错误出在多节点实现部分 , 还是单节点部分 , 较难定位 。

没有足够的测试用例 , 当为了多节点进行修改的时候 , 不能保证的功能实现仍然行为正确 。 假设做一个 100 个节点的项目 , 要 100 天时间的话 , 并非每个节点要 1 天的时间 , 而是第一个节点就需要 30 天的时间 , 当第一个节点做好之后 , 扩展后面是很自然的事情 , 然而如果第一个节点做不好 , 每天都做一个节点 , 每天都把昨天做的设计推翻然后重做 , 怕是 100 天也完不成100 个节点 。 这个例子比较极端 , 然而在我们周围没有发生过吗?

对于程序员来讲 , 做一个好的螺丝钉 , 同样需要开放和扎实 。 所谓开放 , 就是我们虽然仅仅是最最低级的员工 , 可能整个系统的架构根本轮不到我们 , 但是这并不表明我们只盯着自己的一亩三分地 , 完成功能了事 , 而是要经常站在整个项目的角度考虑问题 。
不想当将军的士兵不是好士兵 , 建议做一下几件事情:
在项目的各种会议上 , 经常站在项目经理和架构师的角度考虑问题 , 要是自己会如何设计 , 前辈们为何这样设计 , 然后多问前辈问题 。 虽然最初的想法比较幼稚 , 但可以不说出来 , 但是长时间这样思考 , 会发现自己的设计水平会突飞猛进的 , 慢慢的 , 你会发现你能够在会议中给出一些建议 , 然后你会发现能够发现前辈设计中的一些缺陷 , 然后你会发现你能够对当前的设计提供恰当的改进方案 , 终于有一天你发现你不再是一个单节点的普通程序员了 。

除了完成自己的功能外 , 多看一看前辈们实现过的代码 , 用自己的方式手绘一些他们的架构图 , 记下不太明白的部分及觉得很优秀可以借鉴的部分 。

尝试在自己的模块中(可能最初是很小很小的模块)尝试使用学到架构 。 可以重读或新读一些经典书籍 , 争取能够用业界内公认的理论来解释自己接触到的设计及架构 , 使得从感性认识上升到理性认识 。 比如原来前辈们写这些类 , 用的是这种设计模式 , 它还有以下的优点和缺点 , 适合设计怎么样的系统 。 这样不但有利于我们在以后的项目中恰当的使用已掌握的设计 , 而且也有利于向他人准确的描述项目 。 试想在面试中 , 一个专业术语要比杂七杂八的列一大筐类更显水平吧 。

可以在餐桌上 , 向自己的同学 , 朋友描述一下学到的架构 , 让你的朋友往细节里问 , 不确定的回去再下功夫 , 争取做到虽然你只是项目中的一个螺丝钉 , 但是好像你从头到尾设计了这个系统一样 。

这里要提醒一下大家 , 并不是所有的上司都喜欢要当将军的士兵和老问问题的员工 , 适当把握火候吧 。

所谓扎实 , 就是指对接触到的知识 , 都应该根据实践 , 结合理论 , 由点到面的掌握 。 在这个阶段 , 信息量是很大 , 要学的东西很多 , 往往对各种技术都接触一下 , 又对各种技术都浅尝辄止 , 最后形成样样通 , 样样松的局面 , 阻碍了自己的发展 。 面试的时候也经常发现一些应试者 , 掌握的东西仅仅局限于他做过的那个点上 , 相关知识的掌握非常弱 , 这自然会影响他从一个单节点程序员向多节点发展 。
因而每当在项目中接触到一方面的东西 , 除了上班完成项目外 , 下班后多看一些有关此方面的书 , 博客等 , 使得从知识点变成知识面 , 知其然 , 还要知其所以然 , 并了解存在的问题 。 当白天在 MFC 中拖完控件后 , 总应该读一些《深入浅出 MFC》来了解其机制 , 读一下《windows 核心编程》了解一下 windows API 及一些原理 , 最好读一下《windows internals》了解一下原理背后的故事 , 不然面试的时候如何向别人开口做过 windows 下的程序设计呢?总不 能 够 创 建 过 socket 对 象 就 声 称 会 socket 编 程 吧,至 少 看 一 下 《 UNIX Network Programming》 。 用过 NFS 怎么不把 linux 的 filesystem 的机制了解一下呢?

当然这样是很累很费时间的 , 然而刚毕业的我们 , 没有经验 , 没有人脉 , 没有资金 , 有的不就是时间吗?珍惜刚毕业的这几年多多打实一下基础 , 等年纪大了 , 精力没这么旺盛了 , 很多事情要照顾了 , 还要靠这时候的老本啊 。

总结:项目或程序员的第二阶段:道生一 , 关键词: “开放性”“扎实性”
三、一生二对于项目来讲 , 当单节点系统足够稳定的时候 , 是应该向 client/server 或者master/slave 结果扩展的时候了 , 也即一生二的过程 。 对于程序员来讲 , 当你已经足够胜任个人工作的时候 , 是可以带一个实习生或者初级程序员了 。 此步的关键即\"communication\" , 沟通 。

对于系统来讲 , 主要考虑的应该是节点之间如何通信 , 如何协作 , 采取何种协议 。 通信可以是面向连接的 , 也可以是不面向连接的 。 可以是同步的 , 也可以是异步的 。 通信是分层次的 , 不同的情况应用不同层次的协议 ,heartbeat 用何种协议 , 内部数据块传输用何种协议 , 发布成 service 向外提供服务用何种协议 , 都是应该考虑的 。

数据的内部结构就像接口一样是要通过沟通商定的 , 便于解析又易于扩展 ,rpc? serialized object? xml? json? protocol buffer? 还是自己定义的格式?对于要经常访问的客户端 , 连接池是必须的 , 每次建立连接是很耗时的服务器端应该有对连接总数的限制 , 以及请求的分发 , 拥塞控制(并不一定是网络拥塞 , 而是某个阶段的处理相对较慢)

通信模块在项目中不应该是任何两个需要通信的模块都要开发的 , 而是应该作为基础性模块 , 经过大量的测试后达到稳定 , 使得需要应用通信模块的人员能够集中精力在本身的逻辑上 , 当模块间协作出现故障的时候 , 不用担心是通信模块传错了的问题 。

对于程序员来讲 , 同样要考虑和实习生或初级程序员之间的通信协议问题 。
有的代码自己觉得写的很清楚 , 但是让新手上手的时候 , 如何能够准确的描述你的思路 , 想法 , 设计 , 遇到的困难呢?如何根据对方的反馈确认对方真实了解了你所表达的信息呢?有很多有经验的程序员 , 由于天天面对着电脑而疏于和人的沟通 , 可能会一肚子能耐却说不出来 , 非常可惜;而对于新人 , 他的回馈是懂了可并不一定代表他真的懂了 , 也可能是不懂又不敢说 。

重要的问题的沟通应该是同步的 , 也即面对面沟通 , 这样除了语言上的反馈 , 还能通过表情得到一定的反馈 。 任何问题都要事先划分为若干阶段 , 最好脑子中有张拓扑图 , 后一阶段的理解会依赖于前一阶段的理解 , 一股脑把所有的信息放在对方面前 , 任何人都会晕 。 每经历一个阶段 , 都要收集一次反馈 , 作为信息的发送方 , 可以通过事先准备一些关键点的问题来检测对方是否真正了解 , 作为接收方 , 可以通过\"你的意思是说 。。。\"等以自己的方式重复对方的表达来进行反馈 。 注意拥塞控制 , 每一次的讨论争取一个小时内完成 , 再长效果会下降 , 接受者感觉信息被塞了满满一脑子 , 没有头绪 , 难有清晰的思路了 。

每次沟通都应该至少有会议记录和部分结论 , 不然讨论等于白讨论 , 否则会发生团队经常开会 , 但是总在讨论同样一些问题 , 感觉上好像每次都在头脑风暴 , 其实效率很差 。 对于重要的结论应该是面向连接的 , 也即书面(邮件 , 文档 ,wiki)告知 , 并有书面回复(ok I amfollowing the bug XXX) 。

可以建立一些连接池 , 也即沟通的特定上下文 。 经过一定时间的团队磨合 , 可以下意识的创造或达成共识的一些词汇来代替一定的上下文 , 可以提高沟通效率 。 比如\"明天甲系统出 report\" , 则程序员明白要有单元测试覆盖率报告 ,QA 明白要在当前 bug 的报告 , 性能测试组应该有甲系统性能测试报告 。

沟通也是分层次的 , 最容易犯的错误的无论针对谁 , 写的文档 , 发的邮件都是一样的 。 其实针对不同层次的人 , 应该提供的信息不同 , 对于本团队人员 , 原理 , 架构 , 设计 , 测试 , 每步怎么做 , 结果如何 , 具体数据都应该说明 , 而对于其他团队的人员 , 具体的数据和每步怎么做就不需要了 , 对于项目经理 , 仅仅说明原理 , 架构 , 结论就可以了 , 对于高层来视察工作 , 原理加结果就行了 。 这也是为什么一篇文档有 abstract summary overview concepts detail appendix 等等部分 , 其实是对不同的人员看的 。

总结:项目或程序员的第三阶段:一生二 , 关键词: “沟通”
四、二生三对于项目来讲 , 当 Client/Server 或者 Master/Slave 已经运行稳定 , 是应该开发一个 Master多个 Slave 的时候了 , 即二生三的过程 。 对于程序员来讲 , 当你能够很好的带一个实习生或者初级程序员的时候 , 是可以成为一个小的Team lead 了 。

此步的关键是 load balance , 平衡 。
对于系统来讲 , 负载均衡最重要的是两个目的:
高可靠性:当一个服务器 crash 的时候 , 不至于影响对外提供服务 。
高性能:多台服务器可以并行的做事情 , 提供服务 , 加快相应时间 。

其实说到底 , 负载均衡采用的是 Data partitioning(数据分块)或 Data replication(数据复制)的方法 。 数据分块即按照某种策略 , 将某类请求全部指向某个服务器 , 比如说按照时间分块 , 例如邮件备份系统 , 可以将某个时间段的邮件全部放在一个服务器内 , 对这个时间段的查询全部指向此服务器;再比如按照地区分块 , 例如地图信息管理系统 , 将某个地区的数据全部放在一个服务器内 。 数据复制即将同样一部分数据复制多份 , 放在不同的服务器上 , 既保证高可靠性 , 又能将请求平均的分配给多个服务器 , 例如 Google File system 中将数据复制三份 , 大型网站的服务企业一般会将同一内容放在不同的服务器上 。

对于程序员来讲 , 沟通同样重要 , 但是不再是局限于一对一的沟通了 , 不再是能把问题表达清楚就可以了 , 而是要在团队内部保持平衡 。 无论是工作压力 , 项目有趣程度 , 培训机会 , 成长机会都应该平衡 。

也无非是两个目的:
高可靠性:项目不会因为一个人的生病 , 休假 , 离职而影响项目的进度 。
高性能:整个团队的力量发挥出来 , 不至于一个人成为了瓶颈 。

所采用的不过也是数据分块和数据复制的方式 。 所谓数据分块 , 即不同的人负责不同的模块 , 比如一个负责前端 , 一个负责后端 , 或者一个负责开发 , 一个负责测试等 , 这能够带来高性能 , 因为每个人的专业化和经验会使得效率提高 , 但是同时带来的问题是高可靠性 , 如果转负责这个模块的人离开 , 换一个人将大大降低效率 。

工作压力也不能很好的平衡 , 往往一个系统的初期阶段 , 后端的开发十分忙 , 而前端相对较闲 , 而到了后期 ,前端开发非常忙 , 而后端已经稳定 , 因而较闲 。 况且 , 人不是机器 , 是有边际效应的 , 当负责一个模块时间一长 , 兴趣会大大降低 , 觉得没有成就感 , 成长也少了 。 因而数据复制的方式也是必要的 , 也即通过伙伴开发 , Knowledge sharing ,code review 等方式 , 让不同模块的人之间相互了解对方的模块 , 从而带来高可靠性 , 也即一个人不在 , 其他的人可以较快的跟上 , 也可以在一个模块压力大的时候 , 其他人帮忙做一些辅助的东西 , 比如各种 tool , 测试用例 , 性能测试 , 甚至改一些优先级较低的 bug , 不仅平衡了工作压力 , 而且接触新的模块会使得员工有较大成长 , 也使工作更加有趣 。

总结:项目或程序员的第四阶段:二生三 , 关键词: “平衡”
五、三生万物如果道生一 , 一生二 , 二生三是质变的过程 , 则三生万物是量变的过程 。
对于计算机系统来讲 , 如果一个 master 能够很好的平衡两三个 slave , 则可以很好的扩展到十个甚至百个 , 千个 。 但是原理是理想的 , 现实却是 , 当 master 管理的 slave 的数量达到一定的数目的时候 ,master 就是一个瓶颈 ,master 的高性能和高可靠性又成了问题 , 这时候可以用多个 master 进行数据复制从而负载平衡 , 也可以使得多个 master 每个管理一个 group 的slave , 这时候就应该有 master 的 master 了 , 也即系统出现了分层 。Hadoop 的 Multirack cluster 就是这样的一棵树 。

对于团队的管理也是同样的 , 每个人的直接管理精力在 10 个人左右 , 多于这些人 , 往往会有很多疏漏的地方 , 或者疲惫不堪 , 因而 , 当一个团队成长的一定的程度的时候 , 也是需要分层的 。 当团队增长的 15 至 20 人的时候 , 应该考虑从现有的人员中选出 master , 也即 team lead 或这至少是 coordinator , 使得组织也成为了一棵树桩 。
【程序员|IT 外企那点儿事(5/9):让我们自身像系统一样升级】
总结:项目或程序人生的第五阶段:三生万物 , 关键词: “分层”


    推荐阅读