三十年软件开发之路:老码农的自我修养!

640?wx_fmt=gif

【CSDN编者按】“千帆过尽仍少年”,对于程序员来说,保留技术初心、不断提升实力是夯实自己的不二法则。而本文的作者,作为一名有着三十多年开发经验的“老”程序员,就在本文中详细总结了自己这些年踩过的坑和实践得出的真理,谈到了包括软件开发、团队工作、个人成长等方方面面。相信阅读本文后,会帮助你成为更优秀的程序员。

声明:本文已获作者 Julio Biason 翻译授权。

三十年软件开发之路:老码农的自我修养!_第1张图片

作者 | Julio Biason

译者 | 王艳妮,责编 | 郭芮

出品 | CSDN(ID:CSDNnews)

以下为译文:

这是我30年来从事软件开发过程中所学到的一些实际经验,可能有些听起来愤世嫉俗,但都是我的切身经验之谈。

再次强调,有些内容真的是愤世嫉俗,有些则是对不同工作岗位的长期观察。

 

640?wx_fmt=png

软件开发

 

先明确问题,再开始写代码

如果你不知道你想要解决的问题是什么,那你肯定就不知道要写些什么代码。在编写任何代码之前,先明确地把应用程序是如何工作的写下来。

“如果没有需求或设计,编程就是向空文本文件不断增加bug的艺术。”——Louis Srygley

有时,即使只是“电梯演讲”(指短时间内表述结果内容)那么长——用仅仅两个自然段来描述这个应用程序的功能——也足够了。

有时候我看着自己的代码发呆,不知道下一步该怎么做,其实往往是因为下一步本来就还没有被定义出来。一般出现这种情况,就意味着是时候停下来,与同事们讨论一下了——或者重新考虑解决方案。

将步骤写为注释

如果你不知道如何开始,请先用自然语言、英语或你的母语描述应用程序的流程,然后用代码填充注释之间的空白。比这更好的做法是:将每个注释视为一个函数,然后编写出能完全实现其功能的代码。

Gherkin是帮助你了解期望(expectation)的好帮手

Gherkin是一种测试描述格式,它指出“鉴于系统处于特定状态,当发生某些事情时,这是预期的后果”。即使你不使用任何能读取Gherkin的测试工具,它也会让你很好地理解应用程序的预期效果。

单元测试很好,集成测试更好

在我目前的工作中,我们只测试模块和类(例如,我们只为视图层编写测试,然后仅测试控制器层,依此类推)。它能让我们了解到某一部分有没有出错,但缺乏对整体的观察——而集成测试测试了整个系统的行为,在这方面会表现得更好。

测试可以让API更好

我们在不同层次中编码:有一个存储层,应该使我们的数据永久存储;有一个处理层,应该对存储的数据进行一些转换;有一个视图层,它有关于数据必须如何被展示出来的信息......等等。

正如我所提到的,集成测试感觉更好,但是单独测试不同层可以让你更好地了解其API。然后你可以更好地了解如何调用东西:API是否太复杂了?是否需要保留大量数据才能进行一次调用?

做你知道如何在命令行上运行的测试

也不是说命令行对于任何项目都很重要,但是当你知道运行测试的命令时,你就知道如何让测试的执行自动化起来,然后你可以在一个连续的集成工具中使用这些测试。

时刻准备好扔掉你的代码

很多人在刚开始使用TDD(测试驱动开发,Test-Driven Development)时,一旦被告知他们可能不得不重写很多东西,就会变得很生气。

TDD“旨在”扔掉代码:越了解你的问题,那么你就会越明白,无论你写了什么,从长远来看都无法解决问题。

所以你不应该担心这个。你的代码不是一面墙:如果你必须永远抛弃它,那也不是白白浪费了材料。当然这意味着你编写代码的时间一去不复返了,但是你现在对这个问题有了更好的理解。

好的语言生来带有综合测试

可以肯定的是,如果一种语言在其标准库中自带一个测试框架——即使小得不能再小——那么与没有测试框架的语言相比,它周围的生态系统仍将拥有更好的测试,无论该语言的外部测试框架有多好。

未来思路是垃圾思路

当开发人员试图解决问题时,他们有时会试图找到一种方法来一下解决所有问题,包括未来可能出现的问题。

但现实就是这样:未来的问题永远不会到来,你最终要么必须维护一堆永远不会被完全使用的庞大代码,要么得整个重新写,因为有一大堆屁用没有的东西......

解决你现在遇到的问题,然后解决下一个,然后再下一个。直到有一天,你会发现这些解决方案中显现出了一种固定的模式,然后你才能真正地“一次性解决所有问题”。

文档是写给未来自己的情书

我们都知道,为函数、类(class)和模块编写该死的文档是一个痛苦的过程。但是以后当你看到文档就能回想起来当时你编写函数时的思路,你就会明白将来文档能在关键时刻救你一命。

功能文档是份合同

当你以编写文档作为自己编程工作的起始点时,你实际上是在签订合同(可能是跟未来的自己):我说了这个函数要做这件事情,那么它就必须做这件事情。如果稍后你发现代码与文档不匹配,那你就是代码出了问题,而不是文档出了问题。

如果一个函数的描述包含“和”,这就是不对的

一个函数应该且仅应该做一件事,真的。当你编写函数文档并发现你写了“和”这个字的时候,这意味着该函数不仅仅是做一件事。那么就需要将该函数分解为两个独立函数并删除“和”。

不要使用布尔型变量作为参数

当你设计一个函数时,你可能会想要添加一个flag——不要这样做。

现在,让我给你举个栗子:假设你有一个消息传递系统,并且有一个函数可以将所有消息返回给用户,称为getUserMessages。但有一种情况是需要返回每条消息的摘要(例如,第一段)或完整消息,因此,你添加一个名为retrieveFullMessage的flag/布尔参数。

再说一次,不要这样做。

因为任何读你代码的人都会看到getUserMessage(userId,true)并想知道这里的true到底是个什么意思。

你可以将函数重命名为getUserMessageSummaries并使用另一个getUserMessagesFull或类似的东西,但每个函数只调用原始的getUserMessage为true或false——但是类/模块外部的接口仍然是清晰的。

但是一定“不要”在函数中添加flags / Boolean作为参数。

注意界面的变化

在上面几点中,我提到了重新命名函数的问题,如果你能控制使用该函数的整个源头,那就不算是问题,只需要搜索和替换即可。

但是,如果该函数实际上是由库公开的,那么你不能随便地更改函数名称。这将打破你无法控制的许多其他应用程序,并惹恼其他人。

你可以通过文档或某些代码功能创建新函数并将当前函数标记为已弃用,然后,经过几次释放后,你终于可以Kill掉原来的函数了。

 

(你可以做的一个有些混蛋的举动是创建新函数,将当前函数标记为已弃用,并在函数开头添加一个休眠,这样一来使用旧函数的人会被迫更新。)

好的语言自带集成的文档

如果语言有自己的方式来记录函数、类、模块或其他,而且带有一个哪怕最简单的文档生成器,你就可以确切知道所有的函数、类、模块、库、框架都具有良好的文档了(不是说一定特别好,但至少是比较好的)。

大多数情况下,没有集成文档的语言,文档方面做得都不怎么样。

一门语言绝不仅仅是一门语言而已

编程语言就是你写的、而且能做事情的东西,但在特殊关键词以外它还有很多别的东西:它有一个构建系统,它有一个依赖控制系统,它有一种使工具/库/框架互动的方式,它有一个社区,它有一种与人打交道的方式。

不要仅仅因为一种语言容易使用就选择它。永远记住,你可能因为一种语言的语法很简明而支持这种语言,但是与此同时你也是在支持维护人员对待这个社区的方式。

有时候,宁愿让应用程序崩溃也不要什么都不做

虽然这听起来很奇怪,但即使在处理过程中添加了某些错误,也不要默默地捕捉到错误但什么都不做。

Java中一个可悲的常见模式是:

 

 
try {  something_that_can_raise_exception()} catch (Exception ex) {  System.out.println(ex);}
  something_that_can_raise_exception()
} catch (Exception ex) {
  System.out.println(ex);
}

 

这看起来跟处理异常没有什么关系——除了重复了一遍,仅此而已。

如果你不知道如何处理它,那就随它去吧,你早晚会知道它会发生什么。

如果你知道如何处理该问题,那么就处理它

与前一点相反:如果你知道什么东西在何时会导致异常/错误/某种结果,并且知道如何处理它,那么就请处理它吧。显示错误信息,尝试将数据保存在其他位置,将日志文件中用户的输入捕获到以便以后处理,但要记得处理它。

类型决定你的数据是个什么东西

内存中只是一串字节序列;字节只是0到255之间的数字组合;这些数字的真正含义取决于语言的类型系统。

例如,在C中,值为65的char型变量可能是字母“A”,值为65的int型变量是数字65——处理数据时请不要忘记这一点。这也是为什么大多数人在用布尔型变量做加法以查看True的数量时经常出错。

现在,让我展示一下我最近看到的一个JavaScript里的例子:

 

 
console.log(true+true === 2);> trueconsole.log(true === 1);> falsetrue+true === 2);
> true
console.log(true === 1);
> false

如果你的数据具有模式(schema),请使用结构(structure)来保留它

你可能会想要使用列表(或元组,如果你用的语言允许的话)来保存数据,如果它很简单——比如说,只有2个字段。

但是如果你的数据有一个模式(schema),有一个固定的格式——你应该每次都使用一些结构来保存它,不管是用struct还是class。

理解并保持cargo cult的方式

“Cargo cult”是一种理念,如果其他人那样做了,那么我们也可以。大多数情况下,cargo cult只是对一个问题的偷懒的解决方法:

 

“如果X这样做了,我们为什么要考虑如何正确存储我们的用户数据?”

“如果有某巨头公司是这样存储数据的,那么我们也可以”。

“如果有某巨头公司支持这种做法,那就说明这种方法很好。”

......

不要管所谓的“合适的生产力工具”,你只需要尽力去push进程

“合适的生产力工具”其实意味着:对于某件事情,有一个正确的工具和一个错误的工具——例如,应该使用另外的某种语言/框架而不是当前的语言/框架。但每当我听到有人提到这个词时,他们都是在试图推销他们喜欢的语言/框架,而不是合适的语言/框架。

“正确的工具”比你想象的更明显

也许你当前的项目需要处理一些文本,也许你很想说“让我们用Perl吧!”,因为你知道Perl在处理文本时非常强大。

但你漏掉了哪一点呢?你在一个C的团队工作,每个人会C,而不是Perl。

当然,如果它是一个小的、“放在角落”的不起眼的项目,那么用Perl就可以了——但如果它对公司很重要,那么最好还是用C。

PS:你的英雄项目(本文稍后将详细介绍)可能因此而失败。

不要跟你项目之外的事情纠缠

有时人们会试图改变外部库/框架,而不是使用适当的扩展工具——例如,直接对WordPress或Django进行更改。

这样很容易让你的项目秒瘫痪,变得无法维护。一旦发布了新版本,你就必须与主项目保持同步,并且很快就会发现改动不再适用,你将把外部项目留在一个旧版本中,且充满了安全漏洞。

数据流动比模式更重要

(再次说明,这仅仅是个人意见)当你了解数据如何在代码中流动时,你的代码质量就会更上一层楼,这比无脑应用一堆设计设计模式(design pattern)好多了。

设计模式是用来描述解决方案的,但它不能找到解决方案

(同样,个人观点)大多数时候我看到设计模式被应用的时候,它们被用作寻找解决方案的一种方式,所以你最终会扭曲一个解决方案——有时候,甚至是扭曲问题本身——来适应某个设计模式。

首先,解决你的问题,找到一个好的解决方案,然后你可以检查模式,以提供如何命名该解决方案的思路。

我经常看到这种情况发生:我们有这个问题;一个设计模式接近正确的解决方案;让我们使用这个设计模式吧;现在我们需要在适当的解决方案基础上添加很多东西以适应这个设计模式......

学习函数式编程的基础知识

你不需要彻底搞懂“什么是一个单子(monad)?”和“这是一个函子(functor)?”等问题,但要知道不能一直改变数据——使用新值创建一个新元素(将数据视为不可变),并尽可能使函数/类不保留某些内部状态(纯函数/类)。

认知成本是可读性的杀手

“认知失调”是一种高大上的说法,但其实意思就是“我需要同时记住两个(或更多)不同的东西才能理解这一点。”把这些不同的东西保留在你的头脑中会产生成本,并且事物之间关联性越小,这种成本就越会不断积累(因为你必须把所有这些都记在脑子里)。

例如,将布尔值相加以计算True的数量就是一种轻微的认知不协调;如果你正在阅读一段代码并看到一个sum()函数,你知道它是列表中所有数字的总和,你就预料到列表由数字组成,但我看到过人们使用sum函数计算布尔值列表中的True的数量,这也太特么容易让人糊涂了吧。

Magical Number 7 ,正负二(7+-2的范围内)

“magical number”是一篇心理学文章中提到的概念,意思指人们可以在同一时间记住的事物的数量。如果你有一个函数,它调用一个调用函数的函数,该函数又调用一个调用函数的函数……再往下说下去你可能要疯。

想一想:我会得到这个函数的结果,然后将它传递给第二个函数,得到它的结果,然后传递给第三个函数。但是:

  • 当今,心理学家更多地谈论Magical Number 4,而不是7。

  • 把函数当成写作文(如“我将调用该函数,然后该函数,然后该函数......”),而不是函数作为主体(如“该函数将调用该函数,将调用该函数......”) 。

走捷径挺nice的,但只是在短期内如此

许多语言、库、框架都有缩短工作时间的方法,减少了需要你打字输入的内容。但是,稍后,这些东西会让你栽跟头,你将不得不弃用这些捷径并懂得人间正道是沧桑的道理。

因此,在使用之前,先了解那些捷径是如何做事情的。

你不需要先用难的方式写东西然后再用捷径的方式清理:你只需要走捷径在后台做事情,所以你至少知道使用它可能出错的地方在哪里,以及如何用非捷径方式替换它。

抵制“轻松”的诱惑

当然IDE会帮助你完成大量的自动填充并让你轻松构建你的项目,但是你明白发生了什么吗?你了解你的构建系统是如何工作的吗?如果你必须在没有IDE的情况下运行你的程序,你知道该怎么做吗?如果没有自动填充你能记住你的函数名吗?是不是有办法打破/重命名一些东西让它们更容易被理解?......

要对窗帘后面的东西保持好奇。

总是在你的日期中使用时区

处理日期时请始终添加时区。你的计算机时区和生产服务器时区(或其中一个实例时区)将始终存在问题,你将花大量时间来调试为什么界面总是显示错误的时间。

总是使用UTF-8

在日期上出现的问题,也将出现在对字符的编码上。所以时刻记得将你的字符串转换为UTF8,将它们作为UTF8保存在数据库中,在你的API上返回UTF8。

你可以转换为任何其他编码方式,但UTF8赢得了编码战争,因此更容易保持这种方式。

从笨办法开始

远离IDE的一种方法是“从笨办法开始”:只需获取编译器并获得一个带有代码突出显示的编辑器(任何编辑器),做你该做的事情:写代码,构建它,运行它。

不,这并不容易。但是当你跳进某个IDE时,你看到某个按钮只会简单地想,“是的,它会运行它”(顺便说一下,这正是IDE所做的)。

日志用于事件,而不是用户界面

很长一段时间,我使用日志向用户显示正在发生的事情——因为,你知道的,使用单个东西会更容易一些。

使用标准输出通知用户发生了什么事件,使用标准错误通知用户有关错误的信息,但使用日志来捕捉可以在日后轻松处理的东西。

将日志想成是你必须解析以便在那时从中提取一些信息的东西,而不是用户界面,它不一定要是让人看得懂的明文。

Debugger们被高估了

我常常听到很多人抱怨,不能Debug的编辑器有多糟糕。

但是当你的代码投入生产时,你无法运行你喜欢的Debugger;哎呀,你甚至不能运行自己喜欢的IDE;但是logging......logging随处都可以运行......你可能在崩溃时没有所需的信息(例如,不同的日志记录级别),但你可以启用日志记录以便稍后找出某些内容。

不是说Debugger们很糟糕,只是它们没有大多数人想象的那么有用。

始终使用版本控制系统

“这只是个随便写的破程序,我只想学点东西”——这不是一个不使用版本控制系统的好借口。如果你从一开始就使用版本控制系统,那么当你做了一些傻事时,撤销会更容易。

每次提交一个更改

我见过人们编写提交消息,如“修复了问题#1,#2和#3”。除非所有这些问题都是重复的——那么其中两个应该已经不存在——它们应该分三次提交,而不是一次。

尝试在每次提交中只进行一项更改(并且这里的更改并不是“一个文件更改”; 如果一个更改需要更改三个文件,你应该将这三个文件一起提交。想想“如果我还原这一步,那是什么消失了?“)

当你过度交换时,“git add -p”是你的朋友

(仅限Git的主题)Git允许使用“-p”部分合并文件,这允许你仅选择相关更改并不管其他更改——可能是为了新的一项提交。

按数据/类型组织项目,而不是功能

大多数项目的组织如下:

 
.+-- IncomingModels|   +-- DataTypeInterface|   +-- DataType1|   +-- DataType2|   +-- DataType3+-- Filters|   +-- FilterInterface|   +-- FilterValidDataType2+-- Processors|   +-- ProcessorInterface|   +-- ConvertDataType1ToDto1|   +-- ConvertDataType2ToDto2+-- OutgoingModels    +-- DtoInterface    +-- Dto1    +-- Dto2-- IncomingModels
|   +-- DataTypeInterface
|   +-- DataType1
|   +-- DataType2
|   +-- DataType3
+-- Filters
|   +-- FilterInterface
|   +-- FilterValidDataType2
+-- Processors
|   +-- ProcessorInterface
|   +-- ConvertDataType1ToDto1
|   +-- ConvertDataType2ToDto2
+-- OutgoingModels
    +-- DtoInterface
    +-- Dto1
    +-- Dto2

换句话说,它们使数据按功能分类组织(所有传入的模型都在同一目录/包中,所有过滤器都在同一个目录/包中,依此类推)。

这很好,很有效。但是当你按照数据进行组织时,将项目拆分到较小的项目中会更容易——因为在某些时候,可能你想要做的与你现在正在做的几乎一样,只是有些许小的差异。

 
.+-- Base|   +-- IncomingModels|   |   +-- DataTypeInterface|   +-- Filters|   |   +-- FilterInterface|   +-- Processors|   |   +-- ProcessorInterface|   +-- OutgoingModels|       +-- DtoInterface+-- Data1|   +-- IncomingModels|   |   +-- DataType1|   +-- Processors|   |   +-- ConvertDataType1ToDto1|   +-- OutgoingModels|       +-- Dto1...-- Base
|   +-- IncomingModels
|   |   +-- DataTypeInterface
|   +-- Filters
|   |   +-- FilterInterface
|   +-- Processors
|   |   +-- ProcessorInterface
|   +-- OutgoingModels
|       +-- DtoInterface
+-- Data1
|   +-- IncomingModels
|   |   +-- DataType1
|   +-- Processors
|   |   +-- ConvertDataType1ToDto1
|   +-- OutgoingModels
|       +-- Dto1
...

现在,你可以创建一个仅处理Data1的模块,另一个仅适用于Data2的模块,依此类推,然后你就可以将它们分解为独立的模块了。然后当你有另一个项目也有Data1但也处理Data3时,你可以重新用上Data1模块中的大部分内容。

创建库

我已经见过很多项目要么创建一个包含不同项目的大型存储库,要么保留不同的分支,这些分支不被用作以后加入主要开发区域的临时环境,而作为一个小而不同的东西延续下去了(从上文讲到的模块化角度来说,请想象一下,我没有构建一个重用Data1类型的新项目,而是拥有一个具有完全不同的主函数和Data3类型的分支)。

为什么不将公共部分拆分出来加到库里并在不同的项目中应用它呢?

原因在于,大多数情况下,“因为人们要么是不知道如何创建库,要么是他们担心如何将这些库‘发布’到依赖源中而不致泄露(因此也许你也应该了解你的项目管理工具如何检索依赖项,以便你可以创建自己的依赖项存储库)。”

学会监控

从前,为了理解系统的行为方式,我添加了大量的指标:输入速度、输出速度、中间滞留数量、已处理的数量......这样可以很好地了解系统的行为方式:速度在下降吗?如果是的,那我可以检查正在输入系统的内容以了解原因。在某些时候下降是否正常?......

事实上,在此之后,试图查明一个没有任何监控的系统有多健康就变得很奇怪,仅使用“是否应答请求”来检查系统运行状况不再适用。

尽早添加监控将有助于你了解系统的行为方式。

config文件是个好东西

想象一下,你编写了一个函数,你必须为它传入一个初始值才能开始运行(例如,一个推特用户帐户ID)。但是后来你又必须用两个值来做,所以你就直接用另一个值再次调用了该函数。

使用配置文件更有道理,只需使用两个不同的config文件运行应用程序两次。

命令行选项很奇怪,但很有帮助

如果你将某样东西移动到config文件,你还可以通过添加选项来选择配置文件并公开它来帮助用户。

现在对每种语言的命令行选项都有一些库可以处理,这将有助于你构建一个良好的命令行,并为你的用户提供一个标准的接口。

不仅仅是功能组成,还有应用程序组成

Unix自带“应用程序只做一件事,并且把它做好”的理念。

如今,我说你可以使用一个带有两个配置文件的应用程序,但是如果你需要两个应用程序的结果呢?那时你可以编写一个应用程序,用两个配置文件读取第一个的结果并转换为单个结果。

即使是做APP,也要从原始的东西开始

APP的开发可能会涉及微服务——这很好——但微服务需要一些关于应用程序如何通过线路(协议等)在彼此之间“对话”的想法。你不需要从那开始,应用程序都可以从文件中写入和读取,这样容易多了。

当你了解了网络是如何工作后,再通过电话进行交谈时可能会担心的吧。

优化是面向编译器的

假设你需要更高的性能,你可能很想看看你的代码和“可以在这里挤出更多性能的东西”或“如何在这里删除几个循环来获得更多速度”。

好吧,猜猜怎么着?编译器知道如何做到这一点。智能化的编译器甚至可以删除你的部分代码,因为它始终会生成相同的结果。你需要做的是为代码考虑更好的设计,而不是如何改进当前代码。

代码是为了让人类阅读的、优化面向编译器的,因此,找到一种智能的方法来解释你在尝试做的是什么(在代码中)而不是使用更少的话语来表述。

通过懒惰(评估)

Lisp很久以前就这么做了,而现在大多数语言都是这样做的。

例如,Python有yield语句,它将停止当前函数的执行并立即返回值,只有在再次调用该函数时才会产生新值。如果你将使用yield的函数链接起来,则不需要像保留返回列表的函数那样多的内存。

 

640?wx_fmt=png

在团队/工作上

 

code review并不是为了彰显风格

花点时间进行code review,指出架构或设计问题,而不是代码样式(风格)问题。没有人真正喜欢那些在code review中写“你在这一行中留下空白了”或“括号前缺少空格”的人。

现在,如果你确实发现了架构或设计问题,那么你可以顺便说一下代码风格问题。

代码格式化工具还可以,但它们也不是无往不胜的

团队可能想要避免在code review中讨论样式,因而可能会考虑使用代码格式化工具在提交之前自动格式化代码。

是的,这部分解决了这个问题,但是还有一个小问题:我们人类不像计算机那样能灵活地阅读代码,计算机可读的内容可能无法被人阅读。当然,有人试图在有利于人类阅读的方面创造一些启发式方法,但这并不意味着这些方法正确。

如果你使用代码格式化工具,请使用它来找出最能更改代码的位置。你可能需要简化这一部分的代码,以避免它出现混乱。

代码风格:遵循它就是了

如果你的项目具有已被定义的代码样式,则必须遵循它。有时可能不清楚(“这个结构/类应该是单数还是复数”?),但请尽力遵循它。

...除非代码样式是Google Code样式

(完全个人观点,你不同意也没关系)每次谷歌发布自己的编码风格,都是一场垃圾焚烧。社区之前采用了更好的风格方式,谷歌带来一个与此很不相同的的风格,只是为了能使其在自己名下。

C / C ++只有一种编码风格:K&R

(再次,完全个人意见)其他所有编码风格都是错误的。(笑)

Python只有一种编码风格:PEP8

社区(大部分)使用PEP8风格,遵循它,那么你的代码可以顺利地与生态系统中的其他部分集成。

显式优于隐式

你知道什么是有史以来最糟糕的函数名称之一吗?sleep()。

睡了多久?是几秒还是几毫秒?

对你使用的东西要表达地明确一些,sleepForSecs和sleepForMs并不完美,但比一个单纯的sleep更好。

(当你编写应用程序命令行界面或其配置文件时,请考虑这一点。)

(我可以在这里抛出整个“Python之禅”,但我正在努力专注于讲个人的,直接的体验。)

公司想要专才,但全才在公司待的时间更长

如果你对单一语言了解很多,那么它可能会让你更容易找到一份工作,但从长远来看,一门语言的使用可能会消失,你就需要再学一门别的语言了。适当了解许多门其他语言有助于长远发展,更不用说这可能有助于你想出更好的解决方案了。

“一种不能影响你对编程的思考方式的语言,不值得了解。”——Alan Perlis

很长一段时间,我遵循着一个简单的编程规则:我在家里用来玩的语言不应该是我在工作中使用的语言。这使我能够接触到后来我在工作代码库中应用的新内容。

我通过编写Rust代码了解了泛型如何在Java中工作;我理解了Spring如何完成依赖注入因为我之前有学过如何在C++中实现。

心中有用户

想一想你将如何使用你从用户那里收集的数据——这在当今“隐私”变为一种奢侈的时代更为普遍。

如果你捕获任何使用数据,请记住保护它免遭未经授权的使用。

处理用户数据的最佳安全方法是压根不捕获它

你可以确定,在某些时候,数据会因某些安全漏洞或人为干扰而泄漏。如果你没有捕获任何用户数据——或以匿名方式存储——你将不会遇到任何问题。

记下来那些“让我花了一个多小时才解决的愚蠢失误”

我尝试过,但从未真正建成过一个列表来记录那些需要花一个多小时才能修正的失误,这种失误仅仅是“忘了添加依赖”或“添加注释”一类,可我不止一次与这些愚蠢的失误作斗争了。

但你应该尝试保留一个列表来记录那些让你花了一个多小时才解决的愚蠢失误,因为有了它以后你解决起这类失误来要更快一些。

如果它无法在你的计算机上运行,那么你就有麻烦了

我看过很多系统永远无法在孤立的计算机上运行,比如开发人员工具,因为系统需要在专门的环境中运行。

这真的会扼杀生产力。

如果你的系统将在一个专门的环境中运行——包括“云”——那就去找可以抽象你所用之物的东西。例如,如果你使用的是AWS SQS(队列),请找到一个可以抽象队列工作方式的库,这样你也可以使用RabbitMQ了,就可以在你自己的计算机上轻松运行了。

如果你使用的是非常专门化的东西,你可能必须自己编写抽象逻辑了,将其与主系统隔离,这样你就可以安心开发主要产品。

 

640?wx_fmt=png

个人生活

 

该停下来的时候,就停下来吧

要知道自己什么时候写不动代码了,要知道自己什么时候学不动了......不要强迫自己,不然将来只会使事情变得更糟。

有一次偏头痛时(不严重,但也不算轻),我试着坚持继续写代码。结果第二天,当我好点了的时候,我不得不将大部分重写,因为前一天写的实在太烂了。

CoC保护的是你,而不是别人

当你开始使用任何语言/库/框架时,请检查他们的CoC。这会保护你,不会让你因为没能立马就上手而被别人怼,而不是阻止你告诉别人你的想法。

我提这个因为很多人抱怨CoC,但是他们忘记了正是CoC使他们能加入任何项目而不被白眼,被说是“新手菜鸡”或“先去看完文档,否则别来烦我们”。

此外,请记住,大多数反对CoC的都是那些希望能直接责骂任何人的人。

学会说不

有时,你不得不说:不,我不能这样做;不,在这个时间之前完不成;不,我觉得不能做到这一点;不,我写这个感觉不舒服。

有一次我不得不对我们的CTO说:“好的,我会做的,但我想说明,我不认同我们正在做的事情。”最后,APP刚好就因为我们做的事情而被禁止了。

你负责你代码的使用

这很难,非常非常难,这就是“自由”和“责任”之间的区别。

写代码没有错,例如,用于捕捉人脸并检测其种族的软件,但你必须考虑它将被用在何处。

当还没完成时,不要说“已经完成了”

你厌倦了一遍又一遍地运行同样的事情。有时候即使你记得会发生一些小故障,但是因为你累了,你就告诉大家“已经完成了”。

——不要那样做。有人会在第一次运行时就遇到故障并立即告诉你它不work。

你将从痛苦中了解你自身

我们对无法编译的代码会感到很挫败,也会对客户来回询问一些事情而感到愤怒。当发生这种情况时,我们会迁怒于他人。

生活就是如此,这些都是难免的。

人们之所以会对代码/架构感到生气/烦恼,是出于关心

你会发现自己处于硬币的另一面:你将描述一些解决方案,人们会对某些解决方案感到恼火/生气。当人们关心产品/代码时,他们往往会有这种反应。

“是的,你不喜欢那种安静的解决方案,因为你太在意了”,这是别人对我的最暖心的赞美之一。

从你的烦恼中学习

你会烦恼、生气、沮丧和愤怒,你会看到人们因为这些情绪而陷入困境。所以你必须了解它,不要忽视它。

我从教训中学到的一件事是,当我感到沮丧时,我会变得非常有侵略性。现在,当我注意到我开始感到沮丧时,我会向其他人寻求帮助。看到其他人也在努力解决你的问题,这真的很治愈的感觉。

注意人们对你的反应

我有一个“愤怒男人的休息脸”那样一种脸。

有时候我一问问题,人们就会稍微后退——就好像我在说他们的解决方案是错的一样。那时我必须补充道,“我不是说这是错的,我只是有点困惑”。

这可能会帮助你避免陷入困境。

学会识别那些人格有毒的人,并远离他们

你会发现那些人,即使他们不对你的事情窃窃私语,他们也会对所有事情都说坏话——甚至是说其他人的坏话——而且是公开地说。

远离那些人。

你不知道这种态度会让你情绪多么失落。

谨防微观侵略

“微观侵略”(Micro-aggressions)是每次小剂量的侵略性评论。就像有人一直称你为“那个人”或看似人畜无害地评论你在某些政策中的立场。

这种行为很难反击,因为PR不会听从你说的话认为他们这是在攻击你。而且,这种行为很难被发现,因为它们看起来足够小,但是它们会堆积起来,到最后你会一次爆发你所有的愤怒。

最好远离,尽可能避免接触。

不,我不认为这样的人是“会改正的”

(个人意见)有人可能会说“嘿,也许如果你跟那个人说一下,他们就不会那么做了”。

就个人而言,我认为他们不会。这种东西对他们来说已经太久了,他们觉得很自然,而且大多数情况下,你才是做错的那个人(因为没有get到他们是在开玩笑,例如,真正的“薛定谔的混蛋”风格。)

只有当你意识到自己是那类有毒的人/微侵略者时,才有可能自己改正

除非你意识到你表现得像一个有毒的人或是在微观攻击某人,并且意识到你实际上是在搞破坏,不然没有办法改变这些性格特征(再次强调,个人观点)。

......大多数情况下,听到别人批评的声音可能会让你觉得,“他们跟我过不去!”

英雄项目:总有一天你必须做的事情

“英雄项目”是你个人认为可以解决项目中一系列问题的项目/规范变更/框架。它可能是不同的架构、新的框架甚至是新的语言。

这意味着你将花费你的空闲时间来写一些已经被应用的/已经存在的东西,只是为了证明自己的一个观点。

有时它会告诉你错在哪里。

(但不管如何,你都从中得到一些东西。)

不要混淆“英雄项目”与“英雄综合症”

我至少见过两类这种情况:有人声称项目离了他们就玩不转,或者声称他们不需要任何人的帮助。

这是“英雄综合症”,认为有人可以自己独当一面。

不要做那种人。

知道何时果断辞职

你告诉你的老板你没有按时完成某项工作,因为一些意料之外的原因,他却朝你发飙。

你的一个同事不断微观攻击你。

另一个是那个一直在做愚蠢恶作剧的家伙,不停说废话以及在背后议论其他的小组。

第三个人总是抱怨说,当他不在时,大家的工作就都搞不定了。

现在是时候开始投简历了,无论你目前的薪水有多高或项目有多棒。

......除非你想在四十多岁时还经常被别人惹恼。

IT世界是一个非常小的“蛋”

我们这里有这么一种说法:“某事物的世界是一个小蛋”,这意味着你生活在一个小世界里,世界整体很小。

IT世界真的很小。

记住今天与你一起工作的人,你可能会在15年后与他重逢,这期间你们可能已经各自换过三四份工作了。

你会在中途遇到很多其他的I.T.人。

他们会谈论自己。

无论你说什么/做什么,都会被大家谈论到,一个人会听到并传递给另一个公司,这个公司将传递给其他人,再把故事传递给另一家公司,然后突然,你意识到了,当地没有人会雇用你了,因为每个人都知道你搞砸了一个项目或捶了一个同事的脸。

纸质笔记实际上很有帮助

我曾经多次尝试“无纸化”。在某些时候,我确实不需要用纸了,但是到最后,在你旁边如果有一个小笔记本和一支笔来让你写下你需要发送数据的那个该死的URL的话,真的挺得劲的。

Trello非常酷,但Post-it更好

没有什么比在桌子上放一堆Post-it更能显出你是这样一个人了:“我真的很忙,但我忙中又井井有条”。

在博客中记录你笨手笨脚的解决方案仍然比什么都不写要好

你可能会觉得“我没有准备好谈论这个”或“这太愚蠢了我不应该谈论这个”。

创建一个博客,发布你那些看起来笨手笨脚的解决方案。不管怎么说,它们肯定还是比某些人的解决方案更聪明。

此外,稍后再回来写下更好的解决方案,挑战你自己之前的方案。

以显示你的成长。

除此之外,博客还可以帮助你保存笔记或待办事项。

...但请关闭评论

发布你笨手笨脚的解决方案的一个问题是,这会吸引一些只想惹毛你的人。“这太愚蠢了”,他们会说。可能会有人说,“你好傻”,而他们不知道谁才真的傻。

把评论关了。不要让那些人阻止你。

把你的笨手笨脚的解决方案发布到网上

不要只把那些“很酷,近乎完美”的项目放到Github上,你完全可以表现出来,在某些时候,你只是初学者。

毕竟你总是可以随时返回并修改代码。

(或者说不要:我仍然拥有我的第一个Python项目的公共repo,看起来我刚刚将Java翻译成Python,而不包含有Python特性的部分。)

列出“我不知道的事情”

著名物理学家理查德费曼保留了一本标题为“我不知道的东西”的笔记本。

当你发现一些看起来很酷的东西并且你想知道更多时,创建一个标题为“我不知道的东西”的文件/注释/任何内容都行,然后记下你发现了的/弄清楚了的东西。

别惊讶!人工智能时代即将到来!

https://edu.csdn.net/topic/ai30?utm_source=csdn_bw

原文:https://blog.juliobiason.net/thoughts/things-i-learnt-the-hard-way/

【END】

三十年软件开发之路:老码农的自我修养!_第2张图片

 热 文 推 荐 

三次创业,三次跨界,这次凭十万行核心C代码登上 GitHub Top 1!

苹果 Siri 被曝涉嫌泄露用户隐私;中国联通回应 5G 入网问题;PHP 7.4 beta 1 发布 | 极客头条

我与“顶级工程师”距离有多远?

☞Android 告急!

3个核心差异, 告诉你为什么Libra永远成不了比特币!

微博宕机复盘:什么样的技术架构,可支持80个明星并发出轨?

☞公开课 | 详解事件抽取与事件图谱构建

超酷炫!Facebook用深度学习和弱监督学习绘制全球精准道路图

中国第一程序员,微软得不到他就要毁了他!

640?wx_fmt=gif点击阅读原文,输入关键词,即可搜索您想要的 CSDN 文章。

640?wx_fmt=png你点的每个“在看”,我都认真当成了喜欢

你可能感兴趣的:(三十年软件开发之路:老码农的自我修养!)