最近在 Github 看到这一篇将程序员一直需要使用的非技术核心能力进行了总结,深受里面内容的启发,语言、框架都是会过时的,但有些技能无论是什么语言或者框架都是通用的,如果要在这个行业持续深根,那么这些非技术能力是必备的且实用的。本文在原文上进行翻译,并对文中提到部分专业术语进行了解释,以及对指向外部链接的文档内容作了一些简单的概述,希望能帮助到查看此文档的人。

原文地址:evergreen-skills-developers

中英双文地址:[译]evergreen-skills-developers

原文翻译:

这个仓库包括了一份“常青技能”清单,这份清单应该可以作为对技术精湛的软件工程师/开发者客观评价。

这份工作的是为了在招聘软件开发者/工程师时,提供一个替代的技术面试的方案。文档关注的是软开发最佳实践、跨框架原则和通用的技能;而不是我们在行业中经常看到的语言层面,或者特定技术框架的内容。

编程语言不断进化,公司也不断改变他们的技术栈,框架很快就会过时,有经验的工程师使用搜索引擎能在几分钟就能解决语法相关的问题。因此,在面试候选人时关注这些方面是否有意义呢?

另一方面,技术框架以外的原理和非技术的技能是在谷歌上查不到的,这些技能是“常青”的,并且对工程师的表现有巨大的影响。这些更能反映出软件开发者/工程师为团队带来的真正价值。

这个仓库是基于以下文章的一个衍生作品:”是什么造就了一位伟大的软件工程师“。

这是一个正在进行中的工作。重要的知识可能缺失,现有的条目可能可以改进,更好的分组策略也可能被发现。因此,任何贡献(即PR或问题)都是受欢迎的。请随时按照贡献指南提出修改建议。

目录

非技术技能

以下非技术能力可能是开发者最重要的能力。尽管一个人可能具备很强的技术能力,但在公司中没有良好的沟通、团队合作态度、开发流程、解决问题的能力和学习的心态的话,一切会变得非常糟糕。

核心技能(又称“软技能”)

交流

  • 遵循沟通的最佳策略 (e.g. use threads to organize discussions and other best-practices from Slack)

    两份链接指向的 slack 的一则使用文档和一份 slack 使用技巧文档

  • 最小化干扰

    链接指向的文章是一篇关于程序员在工作中,因被其他事项而中断程序开发的影响,一般人,在工作过程中断打扰后大约需要23分钟才能恢复到之前的状态,而程序员需要更久,文中强调了工作中断对程序员工作效率和心情的影响,并讨论了有计划和非计划性中断的不同影响。

  • 保持礼貌

团队

  • 练习同理心

  • 保持谦逊和低调

  • 做一个积极倾听的人

  • 做一个好的导师

  • 知识分享

  • 得有见地

创新和自我管理技能

开发流程

  • 了解《敏捷开发原则》

  • 适应迭代和增量开发

  • 自组织的能力

    指的是个体或系统能够自发地、无需外部强制指挥,根据内部规则和相互作用来组织自身结构和行为的能力。这种能力在多个层面都有体现,包括个人自我管理、团队协作以及更广泛的社会和生态系统

  • 避免产生错误的预估(比如:工时预估)

  • 关注优先级和业务价值

问题解决能力

  • 使用科学方法(Scientific Method)

    科学方法是一种有系统地寻求知识的程序,涉及了以下三个步骤:问题的认知与表述、实验数据的收集、假说的构成与测试。

  • 检索能力

  • 横向思维

    横向思维,指使用间接的、具有创造力的、不是一望而知的推理方式来解决问题

  • 抽象化能力

  • 创造力

  • 五问法

    五问法关键所在就是,鼓励解决问题的人要努力避开主观或自负的假设和逻辑陷阱,从结果着手,沿着因果关系链条,顺藤摸瓜,穿越不同的抽象层面,直至找出原有问题的根本原因。简而言之,就是鼓励解决问题的人要有“打破砂锅问到底”的精神。

  • 风险管理

心态

  • 不要害怕变化

  • 敢于失败

  • 终生学习

  • 批判性思维 (保持理性,质疑决定,“让事实说话”)

技能能力

通用技术能力

有一些技术知识是永恒的,对任何软件工程师都有关,尽管他们将要从事的具体领域各不相同。为了深入了解他们的资历并了解他们的工程实践有多扎实,你可以和他们就编程原理、数据结构、清晰的代码、源代码管理、技术协作或者DevOps实践等主题进行交谈。如果这些基础扎实,他们可能能够毫无问题地学习你们特定领域的东西。

编程准则

  • 基本流程结构和逻辑代数

  • 面向对象编程

  • SOLID, GRASP面向对象设计

    SOLID(单一功能、开闭原则、里氏替换、接口隔离以及依赖反转)

S 单一功能原则 认为“对象应该仅具有一种单一功能”的概念。
O 开闭原则 认为“软件应该是对于扩展开放的,但是对于修改封闭的”的概念。
L 里氏替换原则 认为“程序中的对象应该是可以在不改变程序正确性的前提下被它的子类所替换的”的概念。参考契约式设计。
I 接口隔离原则 认为“多个特定客户端接口要好于一个宽泛用途的接口”的概念。
D 依赖反转原则 认为一个方法应该遵从“依赖于抽象而不是一个实例”的概念。 依赖注入是该原则的一种实现方式。

GRASP中提到的模式和原则包括有控制器(controller)、创建者(creator)、中介(indirection)、信息专家(information expert)、低耦合性(low coupling)、高内聚性(high cohesion)、多态(polymorphism)、保护变化(protected variations)和纯虚构(pure Fabrication)[2]

  • 函数式编程(纯函数、不变性、递归……)

  • 声明式与命令式编程

    声明式和命令式编程范例只不过是描述在不同抽象层次上编码的流行词。声明式编程关注的是“做什么,而不是如何做”,而命令式编程则关注的是“如何做,而不是做什么”。声明式编程是在比命令式编程更高的抽象层次上进行编程。两者都有其适用的地方,例如在网页开发中使用框架时需要声明式编程,而在设计算法和其他底层需求时则需要命令式编程。

数据结构

  • 基本数据结构(基本类型、数组、矩阵、对象…)

  • 缓存和 memoization

    memoization 没有一个很好的词能翻译,大概意思就是通过存储函数调用的结果,并在再次使用相同输入调用函数时直接返回已存储的结果,从而加速计算逻辑。斐波那契数列就是一个使用 memoization 的例子

  • Hash codes、 tokens、编码(比如 Base64)

  • 栈与堆内存

    链接指向一则在 stackoverflow 提出堆栈相关的诸多疑问,最高数回答解释了堆栈两种内存分配方式的基本概念、操作方式和性能差异,其中栈内存分配方式由于其后进先出的特性和近距离的存取模式,使得其在内存分配和回收上更加高效;而堆内存分配方式由于其动态和灵活的特性,对内存的管理相对复杂,但能够满足更多的内存需求

代码整洁

  • 懂得命名对代码的可读性的重要性

  • 避免过长的方法和类,确保职责被划分到各个方法或者类中

  • 遵循约定来管理项目结构

  • 将复杂的布尔条件提取到命名良好的函数中

  • 尽量编写尽可能自解释的代码(即通过阅读代码就能容易理解代码的功能)

  • 良好的命名和轻量的文档而不是行内注释

    代码注释通常可能会误导人,因为它们经常被用作一种捷径,用来解释一段混乱的代码块的功能,而不是投入时间去重构它以提高其可读性。

    链接的文章主张编写清晰、自解释和可维护的代码,而不是过度依赖注释,同时也承认在某些特殊情况下,注释是有其必要性和价值的。

  • 将文档编写为代码,理想情况下与代码一起,以便于维护(例如,在仓库中的“docs”文件夹中的 markdown 文件)

  • 使用文档来描述“为什么”和“怎么做”(例如,目标、用例、组件、高级架构概述等)

  • 在面向对象编程中,组合优于继承

  • Follow 语义化

  • 了解TDD及其实践(例如,“红色,绿色,重构”)

    TDD(测试驱动开发)是戴两顶帽子思考的开发方式:先戴上实现功能的帽子,在测试的辅助下,快速实现其功能;再戴上测试驱动开发的帽子,在测试的保护下,通过去除冗余的代码,提高代码品质。测试驱动着整个开发过程:首先,驱动代码的设计和功能的实现;其后,驱动代码的再设计和重构。

    • 红色:首先编写一个针对新功能的测试用例,此时由于功能尚未实现,测试用例将无法通过(失败,显示红色)

    • 绿色:接下来编写功能代码,使得测试用例能够通过(成功,显示绿色)。在这个阶段,重点是让测试通过,而不是编写完美的代码。

    • 重构:在测试用例通过后,对功能代码进行优化和重构,提高代码质量,同时确保测试用例仍然能够通过。

源码管理能力

  • CVS(控制版本系统)/ SCM(源代码管理)基础知识:分支、标签、集中式与分散式等

  • SCM与仓库管理/托管的区别(即Git与GitHub之间的区别

  • 理解版本化的重要性

  • Commit 最佳实践

  • 功能分支(短期)

  • 基于主干的开发

  • 依赖管理(包管理器的重要性,依赖地狱的风险等)

技术合作

  • 代码 review 最佳实践

    • 一句话来说就是:在执行代码审查时关注相关部分。目的是学习,而不是指责。
  • 结对编程

    这篇文章主要讨论了结对编程(Pair Programming)的相关主题,包括其风格、时间管理、轮换策略、日常规划、物理环境设置、远程配对等方面。还探讨了结对编程的好处和挑战,以及如何说服管理者和同事采用这种方法。此外,文章还涉及了一些与配对编程相关的细节和常见问题

DevOps 实践

  • 自动化构建

  • 构件仓库和镜像注册表

  • 编写自动化测试

  • 单元、集成和端到端(e2e)测试之间的区别

  • 测试金字塔

  • 持续集成

  • 持续交付与持续部署

  • 功能 Flag 和功能开关

通用技术知识

语言理论知识
优化
并发
  • 竞态条件

  • 死锁

  • 互斥

特定领域技术知识

在某些情况下,您可能希望工程师已经了解某些特定领域,例如前端、后端、架构、基础设施或安全方面。在这些情况下,还有一些跨框架的概念和原则,可用于推动针对每个领域的特定技术知识的内容。

前端开发

  • API通信(不同的架构标准,数据如何传输…)

  • DOM(定义,理解,虚拟DOM…)

  • 浏览器事件

  • 响应式设计(目的,优点,渐进增强…)

  • 客户端渲染(CSR)与服务器端渲染(SSR)

  • 分页

  • 状态管理(相关问题,无状态方法…)

  • MVC 和相关的衍生品

  • WebSockets 网络通信协议

后端开发

  • API设计(不同的架构标准,数据如何传输…)

  • 消息代理

  • 关系型数据库(它们是如何工作的,基本概念…)

  • 非关系型数据库

  • 数据库设计

  • ORM(对象关系映射)

  • 批处理进程 / 定时任务

  • 会话处理

  • 错误处理、审查、日志记录

架构

基础设施

  • 虚拟机与容器

  • 进程与线程

  • 控制器-代理/主副本模式

  • C/S模式

  • IAAS, PAAS, SASS

  • Web服务器

  • 反向代理

  • 负载均衡

  • 冗余

  • 延迟

  • 监控

  • 可监控性

安全

  • 身份和访问管理(IAM)

    • 认证(JWT, SSO)

    • 授权(RBAC, ABAC)

  • 公钥密码系统(例如RSA)

  • 加密协议(TLS, SSL)

  • 最小权限原则

  • DoS / DDoS

  • SQL 注入

  • 中间人攻击

  • XSS(跨站脚本攻击) 和 CSRF(跨站请求伪造)