离线下载
PDF版 ePub版

梦里风林 · 更新于 2018-11-18 07:00:35

个人技能

如何保持活力

创建美丽,有用,聪明的东西的欲望能高度调动程序员的积极性。这是奇妙而令人惊奇的。这种欲望对程序员既不特殊也不普遍,但在程序员中,它是如此强烈而普遍以至于它把程序员与其他角色的人们分割开来。

这有一个现实而重要的推论。如果程序员被要求做一些不美丽/有用/漂亮的事情,他们会斗志低落。做丑陋,愚蠢,无聊的员工可能可以赚很多钱,但最后,乐趣才会为公司赚最多的钱。

很明显,有一些完全由动机技术组织起来的工业适用这里的情况。这些我可以识别的特定的编程中的事情有:

  • 为工作使用最好的语言
  • 寻找机会去使用新技术,新语言,新科技
  • 尝试在每个工程里学习或教授一些东西,即使很小

最后,可能的话,估量个人激励的东西对你工作的影响。例如,修复 bug 时,数一数我完全不感兴趣的 bug 的数目,因为这和仍然存在的 bug 数目是独立的,并且也影响我在最小可能的方式上影响我对我公司的顾客的增值。把每个 bug 和一个高兴的顾客关联起来,对我个人的激励。

如何被广泛信任

值得信任,才能被信任。你也应该让别人了解你。如果没人了解你,没人会为你投票。跟与你亲近的人一起,比如队友,这不能成为一个话题。对你部门或团队以外的人,你通过责任和博知建立信任。有时有人会滥用信任,并要求无理由的喜爱。不要害怕,解释这种喜爱会让你必须放弃什么。

不要不懂装懂。与队友以外的人一起时,你必须清除地区分“当下在我脑子里不懂的东西”以及“我曾经没有认识到的东西”。

如何在时间与空间权衡

没有上过大学的话,你也可以成为一个好的程序员,但你不知道基本的计算复杂度理论的话,你不可能成为一个好的进阶程序员。你不需要知道‘O’的定义,但我个人认为你应该理解‘常量时间’,‘nlogn’,'n²'的区别。你可能可以不靠这方面的知识,凭直觉知道如何在时间和空间之间权衡,但没有这种知识,你不会有一个稳固的理解你的同事的基础。

在设计或理解算法的过程中,算法花费的时间有时候是一个以输入量为自变量的函数。当这种情况发生时,如果运行时间与输入量的对数的 n 倍成正比,我们可以说一个算法的最坏/期望/最好情况运行时间是'nlogn',这个定义和阐述的方式也可以被应用在数据结构占用的空间上。

对我来时候,计算复杂度理论是美妙的,并且与物理学一样意义深远,并且可能还有很长的路要走!

时间(处理器周期)和空间(内存)可以相互交易。工程是一种妥协,这是一个好的例子。它不总是系统的,然而,编码一些东西时更加紧凑可以节省空间,但要以解码时更多的处理时间为代价。你可以通过缓存节省时间,也就是,花费空间去存储某些东西的一个本地副本,但要以维持缓存的一致性为代价。你偶尔可以通过把更多信息放在一个数据结构里来节省时间。但这通常导致小的空间占用,同时复杂化算法。

提高时间空间转换经常把它们中的一个或另一个戏剧性地改变。然而,在你开始做这个工作前,你应该问你自己,你将要优化的是否是最需要优化的?研究算法是有趣的,但你不能让这遮蔽了你的双眼让你看不到这样一个冷酷的事实:优化一些不是问题的问题不会产生可见的影响并且会造成测试负担。

现代计算机内存越来越便宜,因为不像处理器时间,你在达到边界前你不能看见它,但这种失败是灾难性的。使用内存也有隐藏的代价,比如你影响了其他需要被保留的程序,以及你分配和释放内存的时间。在你想要花更多空间去换取速度之前,仔细考虑这一点。

如何进行压力测试

压力测试很有趣,一开始好像压测的目的是找出系统在负载下能不能工作。现实中,系统在负载下确实能工作,但在负载足够重的某些情况下不能工作。我把这叫做碰壁撞响[1]。可能会有例外,但大多数情况下会有这么一堵“墙”。压测的目的是为了指出墙在哪里,然后指出怎么把墙移得远些。

压测计划需要在工程的早期就规划好,因为它经常有助于解释真实期望的东西具体是什么。两秒的网页请求是一个悲伤的失败还是一个了不起的成功?500 个并发用户是否足够?这,当然,视情况而定,但一个人在设计系统时就应该知道满足需求的答案。压测需要足够好地为现实建模,使之足够有用。非常容易地模拟 500 个不稳定并且不可预测的人并行使用系统不是真的可能的,但我们可以至少创造 500 个模拟(用户),然后尝试模拟他们可能做的部分事情。

在压测中,从轻负载开始,然后为系统在一些维度上增加复杂 - 比如输入频率和输入规模 - 直到你抵达那堵墙。如果墙太近了以至于不能满足你的需要,指出哪个资源是瓶颈(这通常是那个主要的资源)。它是内存?处理器?I/O?网络带宽?还是数据连接?然后指出你可以怎么移动那堵墙。记录下移动墙的那个要素,也就是增加了系统可以处理的负载的那个要素,它可能不能真正在低负载系统下产生危害。但通常重负载下的表现比轻负载下更重要。

你必须能够观察几个不同维度,以此来为之构建一个思维模型;单一的技术是不够的。例如,日志经常是给出系统中两个事件间的挂钟时间的好主意。但除非仔细构建,日志不会给出内存使用的可见性甚至是数据结构的大小。相似的,在现代系统里,大量电脑和许多软件系统是合作的。特别是在你碰到那堵墙时(也就是,表现与输入不成线性比例时),这些软件系统可能成为瓶颈。对这些系统的透视力,甚至仅仅对所有参与工作的机器的处理器做测量,都可能是非常有帮助的。

意识到墙的存在不仅对移动墙是非常关键的,而且对于提供预报能力也是如此。这样公司可以得到更高效的管理。


[1] "撞响"

如何在简洁与抽象间平衡

抽象是编程的关键。你应该仔细选择你需要抽象的程度。充满活力的初学者经常创建许多没有什么用的抽象。一个标识是,你是否创建了这样一个类,不包含任何代码并且没有真的做什么事情,除了抽象一些东西。这种抽象是可以理解的,但代码的简洁性的价值必须与代码的抽象价值相权衡。有时候,我们可以看到一种热情的理想主义者犯的错误:在工程的一开始,定义了一大堆的看起来抽象得很美的类,然后他会推测说它们可以处理每一个可能出现的情况。随着项目推进及琐事掺杂进来,这些代码本身变得混乱了。函数体比他们本来该有的样子还要长。空的类是一种写文档的负担,在压力之下,它们会被忽略。如果花在抽象上的精力花在了保持事情简短上,最后的结果应该会更好。这是一种推测编程的形式。我强烈推荐 PAUL gRAHAM[PGSite]的这篇文章'Succinctness is Power' by Paul Graham

有这样一种关于信息封装面向对象编程的有用技能,但有时候它们被带远了。这些技术让一个人抽象地编码并预计变数。然而,我个人认为,你不应该写太多推测性的代码。例如,在一个对象里用增量器和访问器隐藏一个整数变量是一种可接受的风格,这样变量本身就没有暴露,仅仅暴露了很少的关于它的接口。这确实允许了变量的实现的改变不影响调用代码,并且可能对一个必须提供一个稳定 API 的库编写者是合适的。但我不认为这种好处会超过,当我的团队拥有调用代码并因此可以把调用器重构为比原来的更容易时,冗长的代价。四到五行多余的代码会是这种推测性好处的沉重代价。

可移植性也有类似的问题。代码是否应当可移植到不同的电脑,编译器,软件系统或平台?还是简单地传输?我认为,不可移植,短而简单传输的代码比长而可移植的代码要好。把不可移植代码限制在特定的领域是相当方便的,并且当然是一个好主意。比如一个使用了特定 DBMS 的数据库查询的类。

如何学习新技能

学习新技能,尤其是非技术类,是最大的一种乐趣。大多数公司在理解了这对程序员的激励程度时,会更加地有斗志。

人类通过来学。读书和上课是有用的。但你对一个从不写程序的程序员会有任何敬意吗?学习任何技能,你应该把自己放在一个可以练习技能的宽容的位置。学习一个新的编程语言时,在你必须做一个大工程前,试着用它做一个小的工程。学习管理软件项目时,先试着管理一个小的工程。

一个好的导师不是你做事情的替代品,而是比一本书更好的存在。你可以提供什么给一个潜在的导师,作为他的知识的交换?至少,你应该努力学习这样他们的时间才不会被浪费。

试着让你的 boss 给你正规的训练,但必须知道,这通常并不会比,把相同量的时间花在,简单地用你想学的技能玩耍上,要好多少。然而,要求训练比在我们不完美世界里的玩耍时间要容易得多,尽管大量正规训练只是在课程上睡觉,等着晚餐聚会。

如果你领导团队,需要知道他们是怎么学习的,并且通过给他们安排适量的和可以锻炼他们感兴趣的技能的工程。不要忘记程序员最重要的技能不是技术。让你的团队成员有一个机会去玩,锻炼勇气,诚实,以及交流。

学会打字

学会盲打。这是一个进阶技能,因为写代码是如此困难以至于你的打字速度是不太相关的,并且不能削减写代码花费的时间,不管你打字有多好。但是,到了你是一个进阶程序员的时候,你可能花费很多时间在用自然语言给你的同事或他人写东西上。这是对你的承诺的一种有趣的测试,学习这样的东西需要专注的时间,但不怎么有趣。有这样一个传说,当 Michael Tiemann 在 MCC 的时候,人们会站在他的门外面倾听他击键的声音,这种声音如此急促以至于是不可分辨的。

如何做集成测试

集成测试是对已经进行单元测试的各个部分的一种整合测试。集成是昂贵的,并且它出现在测试中。你必须把这个考虑到你的预计和时间表里。

理想情况下,你应该这样组织一个项目,使得最后没有一个阶段是集成必须显式进行的。这比在项目过程中,随着事情完成逐渐集成事情要好得多。如果这是不可避免的,仔细评估。

交流语言

在语法系统里,有一些正式定义的,非编程语言但是交流语言的语言,它们为促进交流而非标准而特别设计。2003 年,最重要的这种语言有:UML,XML, SQL。你应该熟悉这些东西,这样你就可以很好地交流并且决定什么时候去使用它们。

UML 是一个丰富的用图表描述设计的正式系统。它的美丽之处在于它既虚拟又正式,在作者和观众都了解 UML 的前提下,可以容纳大量的信息。你需要了解它,因为设计有时候就是用这种方式交流的。有一些非常有用的工具可以让制作 UML 图看起来非常专业。在很多情况下,UML 太正式了,我自己会使用更简单的箱子与箭头的风格来设计图标。但我非常确定 UML 对你来说至少跟学习拉丁语一样有用(译者注:国外拉丁语使用很广泛)。

XML 是设计新标准的标准。这不是一个数据间交换的问题的解决方案,尽管你有时候会看到它在这种情况下出现。更进一步,它是一种受欢迎的对大部分数据交换的无聊部分的自动化,也就是,把表现结构化为线性序列,还有将其转回一个结构。它提供了一些漂亮的类型和正确性检查,尽管,又一次,实践中你可能需要的只是其中的一部分。

SQL 是一种非常有力而丰富的数据查询和操作语言,而非一种编程语言。它有许多种类,典型地依赖于产品,但这没有标准核心那么重要。SQL 是关系数据库的巧舌弗兰卡。你可能可以也可能不可以在任何领域从对关系数据库的理解中受益,但你必须对它们和 SQL 的语法和含义有基本的理解。

重型工具

随着我们的科技文化的进步,软件技术从不可想象,到研究,到新的产品,到标准化产品,到广泛可用和廉价产品。这些重型工具可以拉动很大的负载,但可能是进阶的,并且需要花大量投资去理解。进阶程序员必须知道如何管理它们以及它们什么时候应该被使用或考虑。

现在在我看来,最好的重型工具是:

  • 关系数据库;
  • 全文搜索引擎;
  • 数学库;
  • OpenGl;
  • XML 解析器;
  • 电子表格。

如何分析数据

当你检查一个商业活动并且发现了把它转换为软件应用程序的需求时,数据分析是软件开发早期的一个过程。这是一个官方的定义,当你,一个程序员,应该集中注意力在写别人设计的东西的代码时,这可能会让你相信数据分析是一种更应该归入系统分析的行为。如果我们严格遵循软件工程范式,这可能是正确的。有经验的程序员会成为设计者,最尖锐的设计者变成商业分析师,因此被冠名去思考所有数据需要,并且给你充分定义的任务去执行。这不完全是对的,因为数据是每种编程活动的核心。不管你在你的程序里做什么,你不是在移动数据就是在修改数据。商业分析师分析的是更大尺度上的需要,软件设计者更加压榨这个比例以至于,当问题在你的桌上落地时,好像你需要做的所有事情是应用聪明的算法,开始移动已经存在的数据。

不是这样的。

不管你开始观察它的是哪个阶段,数据是良好设计的应用程序的主要考虑因素,如果你仔细观察一个数据分析师是怎么从客户请求中获取需求的,你会意识到,数据扮演了一个基本的角色。分析师创建了所谓的数据流表,所有的数据源被标记出来,信息的流动被塑造出来。清晰定义了什么数据应该是系统的一部分,设计师将会用数据关系,数据交换协议,文件格式的形式塑造数据源,这样任务就准备好传递给程序员了。然而,这个过程还没结束,因为你(程序员)在这个周密的数据提取过程后,需要分析数据以用最好的可能方式表现任务。你的任务的底线是 Niklaus Wirth,多种语言之父,的金句:“算法+数据结构=程序”。这永远不是一个独立的自嗨的算法。每个算法都至少被设计去做一些至少与一段数据相关的事情。

因此,由于算法不会在真空中滚动轮子,你需要分析其他人已经为你标记好的数据和必须写入代码的必要的数据。 一个小例子会使得事情更清楚。实现一个图书馆的搜索程序时,通过你的说明书,用户用类型/作者标题/出版社/出版年份/页数来选择书本。你的程序的中级目标是提供一个合法的 SQL 语句去搜索后端数据库。基于这些需要,你有几个选择:按顺序检查每个控制条件,使用一个 switch 语句,或者几个 if 语句;用一个数据控制数组,把它们与一个事件驱动引擎相连。

如果你的需求也包括提高查询性能,通过确认每个项在一个特殊顺序里,你可能考虑使用组件树去构建你的 SQL 语句。正如你可以看到的,算法的选择依赖于你决定使用或将要创建的数据。这样的决定产生高效算法和糟糕算法间的区别。 然而,效率不是唯一要考虑的因素。你可能在你的代码里使用一打命名变量,让它变得尽可能高效。但这样一段代码可能不能容易地维护。可能为你的变量选择一种合适的容器可以保持相同的速度,此外,在的你同事明年看代码的时候,让他们能够更好地理解代码。更多的,选择一个良好设计的数据结构可能允许他们在不重写代码的前提下,拓展你的代码的功能。长久看来,你对数据的选择决定了你结束代码的工作后,它能工作多久。

让我给你看另一个例子,只是一些思想粮食,让我们假设你的任务是找到字典里超过三位的同字异构词(一个异构词必须在同样的字典里有另一个词)。如果你把这当做一个计算任务,你将会结束于无尽的,尝试找出每个单词的所有组合,然后拿它跟列表里的所有其他单词比较,这样一个无尽的努力中。然而,如果你分析了手头的数据,你会意识到,每个单词可能被一个包含这个词本身以及用它的字母作为 ID 的排序数组的记录所代表,这个蛮力算法可能需要运行几天,而小的那个算法只是一件几秒的事。下次面对一个棘手的问题时,记住这个例子。

上一篇: 团队技能 下一篇: 团队技能