软件软件软件

软件开发待遇怎么样?高级软件工程师成长秘诀

2018 年的时候,我开始在彭博社(Bloomberg)工作。从那之后,事情发生了很大变化。我不再是公司里最初级的成员了,而且我还指导过几个工程师,这真是太棒了。这有助于我观察自己与其他人的差别,吸收他们的最佳实践,并发现我不知不觉中已经做得很好的事。

1、移动应用开发工程师,目前主流的移动开发平台系统主要是iOS、Android等,主要集中在移动支付、移动购物、移动旅游、移动社交等方面涌现大量的移动互联网游戏、应用和创业公司,因此对开发人员需求也是水涨船高。移动应用开发工程师工资:1~3年(8K~15K)、 3年以上(20K~35K);

2、前端开发工程师。要求切图熟练、能写JS效果,对前端架构、性能优化方面有深入了解,薪资范围1~3年(8K~15K)、 3年以上(16K~20K);

每年的工作回顾是一个很好的方式来提炼我学到的经验教训。它们对于模式匹配也很有价值。只有我从特定模式观察时,才会发现问题。然后我就开始有意识地跟踪这些模式。

今年的宏观主题是扩大眼界并挑战边界。它还涉及到聚焦视野,以及向去年的章节中增加细微差别。如果你预先读过我去年的评论就更有意思了:你就可以区别出我的成长。

3、后端开发工程师。后端语言包括Java、PHP、C/C++和Python等。后端的发展前景很大,无论是B/S还是C/S,无论是WEB还是原生,或者是智能硬件,后端都会屹立不倒。后端薪资:1~2年(6K~10K) 、3~5年(8K~20K)、5~10年(30K~40K);

这些回顾都从一个问题开始:我如何进一步成长?

借助不同的抽象阶梯成长

进入第二年的时候,我已经准备好所有的基础知识了。我已经摘完了所有低垂的果实,我的成长速度开始变慢。这种感觉很不好。我脑海中的最大问题就是“我如何进一步成长?”

我能做来提高我的编码技能的事情有限。大部分博客都讲,要编写简洁的代码、重复练习、不要重复等等,都是比较细微的建议。几乎没有任何一个博客的建议能对我产生立竿见影的效果。

不过,我确实发现了一些有见地的东西。我在软件开发周期内工作,但是这个周期是更大的一个周期的一部分:产品和基础设施开发周期。我决定接触得更广泛而不是更深入。令人惊讶的是,这种广度使得了解得更加深入。

软件开发待遇怎么样?高级软件工程师成长秘诀(图1)

我从三个大的方向展开:学习我周围的人在做的事情、学习良好的思维习惯、获取新的思考工具。

学习我周围的人在做的事情

由于我不是在一个封闭的系统,这使得我能够更好地理解产品经理、销售人员和分析师的工作。最终,这是一门通过产品赚钱的生意。我们的目标不是编写代码,而不是做一门能盈利的生意。

大多数大公司并不是只做一件事,这意味着在同一家公司有几种不同的赚钱路径。每个人至少在一条路径上——如果他们不在,那么他们不会在这家公司。

跟踪这些路径以及自己所在的路径是非常有价值的。这帮助我明白了自己有多么重要,以及我可以利用哪些杠杆来提高效率。有时候,这是为了让销售工作更简单,这样他们就可以做更多的销售。还有一些时候,这是关于为客户构建一个新功能。还有一些时候,这是关于改进一个不断崩溃的功能。

产品经理是最好的来源。他们知道企业如何赚钱,谁是客户,以及客户需要什么。

在过去的一年里,我与我路径上的每个人都安排了几次会议。这给我的另一个好处是了解其他人的工作的上下文。这使得我可以更好地进行沟通。以正确的方式构思事物的作用是很强大的。

例如,一次谈话帮助我理解了销售部的 Sarah 想要一个批量更新工具。一些公司有许多员工,一个一个地更新他们的信息是一件痛苦的事情。我编写的代码可以减轻 Sarah 的痛苦。

学习好的思维习惯

软件工程需要善于思考并作出正确的决策。编程就是实现这些决策。

思维习惯就是你的大脑经常做的事情。这可能是你看见 Y 发生的时候想到 X,或者将思维工具 X 应用到问题 Y。简而言之,思维习惯有助于更好地思考。

我怀疑自己如果早学会了这些一般技能,我应该能够更好地将其应用到软件工程中。

善于思考

软件工程是一个很好的实践善于思考的领域。反馈回路更短,测量正确性不会花费太长时间。

我潜心研究认知科学。这是一项值得探索的永久技能——无论我最终做什么事情,它都能够帮助我,并在我的一生中都会带来回报。其中一个产出是批判性思维的框架。这是复合的,而复合是强大的。

这其中有很多好东西,我会稍后再谈。它们值得各自单独的章节。

提高日常工作效率的策略

硬币的另一面是让你善于思考的习惯。它开始于注意一天中的小麻烦、会议的低效,然后找出避免这些问题的策略。这些策略性的改进是被低估的。

你决定要做什么,然后让它自动运行,解放大脑来思考更多有意思的事情。当然,那也是一种习惯。

我注意到的一些好习惯:

开会一定要做出决定或着有下一步行动,否则不要离开会议决定事情由谁完成。没有负责人的事情很少被完成的。记录项目期间做出的决策。

这种模式是在回顾期间发现的,因此我很想关注并在下一年收集更多策略。有一位杰出的敏捷大师对我负责,能帮助我更好地遵循这些策略。

获取新的思考工具和思维模型

新的思考工具与善于思考有关,对于软件工程更是如此。思考工具帮助我更好地思考具体的工程问题。

我对此采用了及时处理的方法。只有当我被某件事情困扰时,或者当我发现我的抽象和设计决策不起作用时,我才寻找新的工具。

例如,我最近正在为许多复杂的业务逻辑领域头疼。边缘案例很常见,我们想要设计一个系统来很好地处理这个问题。那时候,我读到了领域驱动设计。我可以立即将它应用到实践中,产生巨大的影响。后来,我更好地掌握了这些理念。我获得了一种关于如何创建企业软件的新的思维模型。

我持续学习并获取新的思维模型的第二种方法是通过阅读Hacker News 上的文章。这些都是非常有意思的想法,其中一些我也已经应用到实践中,但是很多想法没有上面提到的技术高效。我仍然这样做的原因是绘制技术图——这是我了解现有的技术,那么当我遇到问题时,我会知道有一个方法可能会有所帮助。

我获取更好的思维模型的最后一种方法是通过学习新的不同语音。这种多样性很重要。学习lisp 的另一种方言比学习C++03、一种函数式编程语言、一种动态类型语言和一种lisp 的好处要小得多。今天, J 看起来很有趣,我就可以考虑学习。这种一种我以前从未使用过的思维模型。

我从中获取了好多益处。每种语言有其自己的词汇和语法,而词汇就是一种原始的思维模型。这是一种新的视角来看待如何做事情。

当内存管理在你的可控范围内时,你就会理解指针和分配器是如何工作的。当Python 将这些抽象出来,你就会欣赏其带来的复杂性的减少。当函数式编程语言中的maps 和filters 出现,你就知道Python 的for 循环可以如何改进。事实上,这就是列表理解。然后你会发现有些事情用面向对象编程处理是多么简单。没有一种魔幻工具可以适合所有事情。然后你就会明白,尽管如此,你不必更换工具。你可以将一个项目的最佳实践应用到另外一个项目来解决你的问题:例如编写函数式的javascript。原理比表现形式更重要。

总的来说,这就是我这一年所做的。以下是针对具体问题的简介

保护你的空闲

当我说slack,我不是说slack 公司,而是说一个形容词(空闲)。

给我带来高产出和提高生产力的一件事就是“慢一点儿”。想要完成更多事情?慢一点儿。

我的意思是:

我注意到人们总是急于解决问题。这可能是他们之前做过的事情,或者是我们有模板的事情。快速解决问题的感觉非常好。我之前也这样做过!无论如何,在一些非常具体的案例中,这么多是行得通的。

当我在做新事情时,我会花时间了解我正在工作的系统,以及与之密切相关的事情。如果事情太多了,我会尽可能多地学习。每次我重温系统,都想了解更多东西。

当有空闲的时候,你就有机会去实验、学习和思考。这意味着你有充足的时间来完成任务。

没有空闲的时候,截止时间很紧张,你的全部精力都集中在完成这件事上。

保护你的空闲意味着不要让截止时间拘束你。通常,这和沟通一样简单(或者困难)。

空闲可能有一种负面的内涵“闲人”,但是保护空闲是非常重要的。这是一种以短期效率为代价的对自我成长的长期投资。

当我快速地交付代码时,我也会花很多时间来修复bug。我没有花费时间来创建系统的合适的思维模型,这意味着我的设想与代码不匹配,而这种不匹配是大多数bug 产生的地方。

我保护自己的空闲,因此我能够花时间来优先学习东西而不是做东西。

我最喜欢利用空闲来进行实验。有时候,我会发现一个对我来说完全没意义的bug。我发现自己有点困惑,然后在Stack Overflow 上找到答案,然后再继续。然而,这个问题会一直困扰我知道我理解这个bug。Stack Overflow 回答了我的问题,但没有解释我的理解有哪些错误。为了提升我的理解,我需要进行实验。

如果我没有空闲,我没有时间去实验,这意味着我必须忘掉这个bug。当有空闲的时候,我能够进行实验来发现我的理解到底哪里错了。当我发现了这个系统的一些新东西时,我喜欢这样的时刻。这会让我下次更有效率。

问问题

我们通常很不擅长问问题。或者是因为我们害怕这些问题会让我们显得很愚蠢,所以我们根本就不问这些问题,或者我们扯很多废话来问这些问题来避免我们显得很愚蠢,而不是学习更多东西。

问题是,在你找出答案之前,你无法判断一个问题是否愚蠢。我回避这个问题的方法是声明我会问很多问题。这让我得以解脱,从底层开始修补我认知上的漏洞。积极的团队文化会对此有帮助。

例如,以下是我学习打包软件的过程:

问:什么是软件包?

答:那是被打包到一起,可以被安装到系统上的代码。

问:为什么我需要软件包?

答:它们提供了一种一致的方法,可以将你所需的所有文件放在正确的位置。没有它们,东西很容易一团糟。你需要确保每个文件都在它该在的地方,设置了系统路径,并且依赖包可用。

问:软件包与我可以在自己的系统上安装的应用程序有什么不同?

答:想法很相似!Windows 安装包像是一个包管理器,能够帮助安装应用程序。类似地,DPKG 和rpm 包有点儿像.exe文件,可以安装在Linux 系统上。你可以借助于 apt和yum包管理器,它们有点儿像 Windows 安装包。

问:我明白了。所以 python 中的setup.py如何转换成一个dpkg?那是如何工作的?

答:我们有一个 python-debhelper,运行setup.py来进行转换。

答:哦,真有趣!你是怎么知道的?

答:debian/rules文件包含如何创建一个dpkg的说明。我看了它就弄明白了。

然后我就知道,我该自己看看这个文档了。我有足够的细节来理解大纲。事实证明,这并没有我想的那么简单,问这个问题也不蠢。

这是我养成的一个思维习惯,而且你可以经常问一些好问题。大多数问题都是依赖上下文的,但是我有一个比较喜欢的一般性问题。

这就是:你是如何发现 X 的?

当我问了一些都弄西,并且他们回答之后,我问的下一件事就是他们是如何知道的?这能帮助我下次自己解决问题。我做了上面的问答,让我了解了debian/rules文件以及它是如何工作的。

另一个可以问的好问题是你有哪些困惑。

发现困惑

有一天,我在使用 Python 中的 datetime。这些是我们的搜索引擎会索引的日期,我们希望它们是 UTC 格式。因此,我修改了我们的 pipeline 来在存储之前将日期转换成 UTC 格式。这就需要知道这些日期的时区。

我创建了一个这样的 datetime:

import datetime from pytz import timezone indexed_date = datetime.datetime(2019, 11, 20, 12, 2, 0, tzinfo=timezone('Asia/Kolkata'))

在这个测试中,这个转换偏差了 23 分钟而失败了。我当时没有注意到,但看到这个让我很困惑。所以,我将测试偏移量设为 -23 分钟,这样这个测试就会通过了。

这是一种非常糟糕的思维方式。一旦我注意到了这点,我就再也看不到这点。有时候我还是会让这些曾经通过的问题困扰。

当然,有些人在 PR(译者注:pull request,拉取代码操作)时用“这看起来不对”来评论——这让我从我的固有思维中跳脱出来,去真正地找出哪里出了问题。

这是一个非常经典的 bug。Pytz 在每个时代都有不同的时区信息。在 1942 年之前,亚洲 / 加尔各答(Asia/Calcutta)的时区是 +5:53:20。(是的,连城市名称都不一样)。当 pytz 时区被传送到一个新的日期,没有参考日期来匹配该年的那个时区。因此,它默认为第一个可用的时区——而这实际上是错误的。其文档中也提到了这点。正确的方式是使用 tzinfo.localize(),将日期匹配到相应的时区,因为正在进行转换的是 pytz 日期。

import datetime from pytz import timezone tz=timezone('Asia/Kolkata') indexed_date = tz.localize(datetime.datetime(2019, 11, 20, 12, 2, 0))

如果 PR 评论没有提醒我,我可能发现不了这点。这暴露了我掩盖困惑这种可怕的思维方式。从那以后,我一直很谨慎。

为了防止这点再次发生,我开始训练我的“注意肌肉”。这叫做注意困惑。不仅仅是写代码的时候,而不是处理任何事情时,都有粉饰疑惑掩盖问题的倾向。

每次你听到一些听起来很奇怪的东西,你都急于解释它为什么一定是真的,你就是在掩藏困惑。关于这点我还写了更多东西。

一旦你开始注意困惑,你就会问一些让你困惑的问题。上一节可能听起来有点老生常谈,但是我希望本节能有所帮助。最难的是注意到什么让你困惑。

鼓励师

在一次冲刺中,我意外感受到了鼓励的力量。

鼓励给予绝地武士力量。这是一种由万物创造的能量场。它围绕着我们,浸润着我们;它们将银河系联结在一起。
——欧比 - 万 - 克诺比(译者注:Obi-Wan Kenobi,《星球大战》中的神秘绝地大师)

我认为欧比 - 万 - 克诺比领悟到了一些东西,尽管是在错误的领域。这是我在软件工程中可以利用的东西:成为一个鼓励师。

那次冲刺,我自己其实没有做很多事情。我写的代码很有限。相反,我在协调哪些变更应该在什么时候进行(这是一个很复杂的冲刺),测试它们是否工作良好,做了很多代码评审,提了很多候补设计建议,并在任何我可以解决问题的地方结对编程。我们完成了所有事情,而且,扩宽视野有助于更容易地进行 PR 决策。这是我们速度最快的冲刺之一。

鼓励给予工程师力量。这是一种由万物创造的能量场。它围绕着我们,浸润着我们;它们将代码系联结在一起。
——尼尔·卡卡(译者注:Neil Kakkar,作者本文)

好吧,我不会再延伸这个比喻了。^_^

对于我来说,如何成为一个鼓励师比如何成为一个 10 倍开发者更有价值。在实践中,团队文化是一个很好的鼓励师(或者泄气者)。

就像我可以创造思维习惯来增加我的产出一样,整个团队也可以。团队文化就是这样。回顾、评审和实验是一个团队为塑造他们的文化所做的内容。这个文化经常是变化的,因为团队成员来来走走,会增加他们的个人感觉。

增强能量的文化是一个鼓励师。我之所以能够做到上面所说的,正是因为我们的文化允许。我们的团队文化关注的是整个团队对冲刺的产出,而不是个人的产出。这允许我为了团队进行提升,而不是专注于我自己。

团队塑造文化,而文化又改造了团队。

这个理念也可以延伸到城市和国家:

一个不断受到军事威胁的社会将有一种崇尚军事优点的文化,一个以合作经济为特点的社会将强烈侮蔑懒惰,一个平等主义的社会将把专横视为一种主要的人格缺陷,一个工作日程安排高度严格的工业社会将重视准时,等等。——为什么文化会获胜

担当责任

我们在 BNEF 有 3 个团队,我们共享一个 Jenkins 自动测试平台。可以预见有一个很大的 Jenkins 维护任务,而我选择负责这个任务。这意味着要弄清楚如何做事情,安排会议讨论改进和替代方案,并且最后协调实施。

但是,当我选择负责这个任务时,我对要做的这些事一无所知。我只是觉得很有趣。

我在我们的群聊中发信息沟通我想出的替代方案。这个沟通很快就没音信了,可能是因为每个人都在忙些什么。我有一种“我不知道我现在该做些什么”的感觉。所以我决定继续我的其它冲刺任务。

我这时的本能反应是“哦,好吧,我试过了。总有一天有人会回信,然后我们就可以继续沟通”。我扮演了负责人的角色,但是并没有负起责任。

当我意识到这点时,我很惊讶。这是一种非常糟糕的管理方式。

每个人都在忙事情,那是他们正在考虑的事情,而不是我的事情。所以,我有责任来将他们的注意力转到要沟通的问题上。

在最初聊天的两天后(我用这段时间来反思并发现自己错了),我再次发信息解释我的决定,以及将分配哪些工作给哪个团队。这是我第二次惊讶的时候:每个人都同意了。并不是他们不在乎,而是他们在第一次聊天之后没有要补充的了。

我非常珍惜这次经历。它教会了我一些重要的习惯:经常跟进,而且如果你负责一项任务,那么推进这项任务就是你的责任。不要在其位不谋其政,而是要真正把事情做好:不管是授权做还是自己做。

这也强化了一个原始习惯:珍惜惊讶。惊讶是一种你的预期与实际发生的匹配的衡量。这是改变你的思维的一个绝佳机会。

拥抱担忧

好吧,最后一个故事。去年,我参与了一个失败的边缘项目。在那个项目中,我学了一种新语言、一种新的做事方式并且测试了一种产品假设。在那个项目中坚持下来真是出人意料的艰难——每次我想起那个项目都会感到害怕。

这种强烈的感受是我无法忽视的。它使我开始注意同样的微妙感受,特别是在工作中的。每当我遇到一项艰巨的任务并且我还不知道如何去做的时候,这种感受就会悄悄地回来。“啊,这要怎么搞?我完全没有头绪。”

我已经学会去拥抱这种感觉。这令我兴奋。这会告诉我将要学习什么东西。到目前为止,我已经开始在我的人体日志中跟踪这种感受——“我这周感到害怕了吗?”如果很多周的结果都是“否”,那我就过得太舒适了。

这种注意到大脑中正在发生什么的原始技能是一种非常强大的监测和诊断工具。就像定期检查系统健康的定时任务那样,复查并改善你的健康:精神上和身体上。这也是本文的目的:这是我的年度工作复查。

增加细微的差别

如果不在过去几年的章节中添加一些细微的差别,这篇文章就不完整。你可以通过这里的链接查看去年的文章。

编码高级软件工程师成长秘诀

在软件工程行业有一个有趣的习惯,即简单地从 Stack Overflow 复制代码。当新手工程师开始相信这个段子时,这是很危险的。当我们说“从 Stack Overflow 复制”时,正在发生的细节都丢失了。

这里有一个从 Stack Overflow 复制的示例。假设我要枚举一个 generator 的所有排列时。那么:

这不是一个代码面试,所以我可以寻找库来帮我实现。但我还不知道使用哪个库。我在谷歌上搜索这个问题,然后发现可以使用itertools.permutations([1,2,3,4])来生成一个列表的所有排列。好吧,太棒了!所以现在我将 generator 转化成一个列表,复制这段代码,并传入这个列表。我就做完了。

现在,我们假设产品需求是按字典顺序对这些进行排序。所以我写了一个处理二阶列表的排序函数。

但是,它不起作用。我发现permutations返回了一个元组列表,因此我返回我的排序函数,并将它改成一个处理元组列表的排序函数。

过了一会儿,产品又有了新需求:这些排列太长了,而且我们想让处理的速度更快一点儿。无论这个列表多大,我们只需要长度为 4 的排列。

啊。好吧。由于我已经有了一个生成所有排列的函数,因此我使用这个函数并从每个排列元组中取前 4 个元素。我意识到这会导致重复,因此我将这些元组放到一个 set 中,然后应用排序函数来使它们按正确的顺序排序。

现在我又做完了。哦,这真是辛苦,但是嘿,每个人都有点开心!这个排列函数对于长列表还是很慢,因此我添加了一条日志,以便某个时候再来看看这个问题。

如果我花时间去查看itertools.permutations的文档,去理解它是如何工作的,我就会注意到:它有一个参数可以决定你想要的返回的排列长度。它返回一个元组列表。而且它返回的时候是排过序的。此外,输入参数不是一个列表,而是一个 iterable,因此我本可以传入 generator。无论如何,它都会被转换成一个元组,因此传入列表还是generator 都没有关系。

这个例子可能看起来微不足道,但是这背后的思维机制却并非如此。我注意到,每当我遇到复杂的API 和误导的命名时,这都会发生在我身上。

简而言之,我的规则是“我不写我不理解的代码”。就像“从Stack Overflow 复制”的习惯一样,这条规则包含许多隐性知识,而这些知识在翻译过程中会丢失。例如,理解代码是什么意思?

至少有三个层次的理解:你可能完全理解 itertools.permutations会产出什么,你可能理解它是如何工作的,或者在更深的一个层次,你可能理解它的实现决策为什么是那样的。

层次 1 是理解函数或 API 是做什么的。

层次 2 是理解它是如何实现的。

层次 3 是理解它为什么是那样实现的。

对于设计良好的 API 和你不想深入学习的东西,级别 1 就可以了。

然而,级别 1 是最低要求。层次 0 就是我们在上述例子中看到的,这是有问题的。另外一个例子是第一次复制现有团队模板,这介于层次 0 额层次 1 之间。

是的,这是一种权衡。层次 0 非常快,而达到层次 3 需要很多时间。

当我不复制粘贴现有模板时,我的速度就会降下来。但当我有足够空闲时,我选择在写代码之前达到层次 1 理解。这通常意味着我第一次的时候会很慢,但是随着时间的推移,我会变得更快。每次我都会加深一点儿自己的理解,而且这有助于我快速地解决 bug。我把学习放在完成事情之上。

有时我也会打破这个规则。一些情况下需要快速简单的修改方式。

有时候,开源文档很烂。这些时候,你需要层次 2 的理解来给你层次 1 的理解:你需要去阅读源码。每当我必须这么做的时候,我都记得为将来的我保留上下文。理解别人的代码是很难的,特别是如果它是用你不熟悉的语言编写的时候。最好不要重复做这种辛苦的工作。当你发现有些东西很重要,把它写下来——那就是需要评论的点。另外,你的团队会为此感谢你。这是一种建立力量倍增器的简单方法。

这很像“存”信息包。它们是你已经完成的工作单元,因此下次你不需要再做了。

理解层次也适用于你的团队拥有的代码,而不仅仅是你复制粘贴的代码,或者从其他人那里“继承”来的代码。理想情况下,你应该对你团队的代码有一个层次 2 的理解,对你自己的代码有个层次 3 的理解。这种理解构建了代码如何工作的思维模型。

未经允许不得转载:软件 » 软件开发待遇怎么样?高级软件工程师成长秘诀