用于我的项目和团队的风格指南和最佳实践的集合。

0.6.0 2024-05-31 20:24 UTC

This package is auto-updated.

Last update: 2024-08-31 00:52:04 UTC


README

用于我的项目和团队的风格指南和最佳实践的集合。

Build Licence

这份文档将不断更新,在接下来的几个月内会有很多变化,因为我将收集更多信息来添加这里。当我完成时,我希望最终产品看起来像这样:[https://codeguide.co/](https://codeguide.co/)

工具配置

您可以在 styles 目录中找到各种语言风格/格式化工具的配置文件。

NPM 安装

npm install --save-dev justintime50-styles

Composer 安装

composer require --dev justintime50/styles

通用

架构

  • 简单设计
    • 运行所有测试
    • 不包含重复内容
    • 表达程序员的意图
    • 最小化类和方法的数量
  • 在临时脚本或SQL查询之上构建干净的接口。这将确保一致性、安全,并且可以进行代码审查
  • 将构建系统与使用系统分开,这意味着“设置”代码应该与主要业务逻辑分开
  • 抽象你的数据结构。抽象接口允许其用户在不了解其实施方式的情况下操纵数据的本质
    • 例如,“车辆油箱”接口,我们公开了油箱容量和汽油加仑数变量。更好的方法是简单地公开剩余燃料的百分比(高级抽象),这样如果需要,我们可以随时更改底下的实现
  • 对象公开行为并隐藏数据。数据结构公开数据,没有显著的行为
  • 在创建软件时,假设你是为其他人创建的。允许配置,尽可能使用CLI标志/环境变量,说明如何使用你的工具,使用简单的接口,并构建它,以便任何人都可以使用,而不仅仅是你和你的一个用例
    • 保持内部变量私有,这样你就可以随意更改其实施类型。创建稳定、一致的接口,让用户可以插入。并非每个变量都需要getter和setter
  • 使用Model, View, Controller框架(MVC),特别是对于前端网站
  • 尽可能使用Singleton设计模式
  • 路由 -> 视图/操作 -> 服务 -> 仓库 -> 数据库
  • 使用前端和后端验证,这很重要,因为我们有两者时,我们可以跳过发送API调用,例如当我们知道有坏数据时。然后我们只有在数据有效时才发送有效负载。后端验证很重要,因为它是真理的来源,因为像浏览器中的开发者工具这样的东西可以击败前端验证

清单

  • 确保你编写的代码是NULL安全的
  • 确保你编写的代码要么有单元测试(尤其是当你需要防止回归时),要么你对其进行了端到端测试。最好是两者都做到
  • 确保代码可读,记住,你将会有其他贡献者未来查看这些代码,甚至可能几年后
    • 它现在有意义吗?
  • 确保你的代码易于维护。是否可以轻松添加内容,当需求在未来发生变化时,我们是否可以调整配置,这对于没有背景的人来说,是否足够明确,以便他们能够随时介入并开展工作?
  • 确保你的代码保留git blame(例如:尾随逗号,将列表拆分到不同的行)
  • 确保你的代码是自我说明的(例如:函数和变量名给出对读者发生什么以及它正在做什么的明确指示)
  • 确保你的代码不会引入副作用。函数和类应该只做一件事情,而不涉及其他领域

  • 类变量应首先列出公开静态常量,然后是私有静态变量,接着是私有实例变量。很少有必要有公开变量
  • 类的名称应描述其承担的责任
  • 你应该能够在25个词或更少的单词中定义一个类,而不使用“如果”、“和”、“或”或“但是”,否则它可能承担太多的责任
  • SPR:单一责任原则,类应该只做一件事情。一个“仪表板”类不应该获取版本号并显示应用程序的统计数据
    • 将版本数据拆分到单独的类中
  • 程序应由许多小类组成
  • OCP:开闭原则,类应该对扩展开放,但对修改封闭
  • DIP:依赖倒置原则,类应该依赖于抽象,而不是具体细节
  • 使用实例变量是一种强大的方法,可以在应用程序中存储在 self 命名空间内时减少传递大量参数,它可以很容易地从任何实例方法中引用,并且可以与 self 关联看似无数个变量(例如:Python)

注释

  • 当我们无法正确表达代码中的意图时,应该使用注释
  • 注释应该解释我们为什么要这么做
  • 注释可以持续多年,确保它们在数百次提交后仍然适用

并发

  • 将与你相关的并发代码与其他代码分开
  • 限制任何可能跨线程共享的数据的访问
  • 对线程中使用的数据进行复制,尽量不在线程中编辑数据,因为这可能导致死锁和同步问题
  • 尽可能保持线程的独立性
  • 避免在一个共享对象上使用多个方法
  • 尽量保持同步部分尽可能小
  • 优雅地关闭系统很困难。开始尽早规划这个并发难题的一部分
  • 不要忽视并发中的偶然失败,这通常是系统构建不完善的指标
  • 使基于线程的代码“可插拔”,其中逻辑和线程操作是分开的,并且可以轻松配置

数据库

  • 所有插入/更新必须在事务中运行。在提交更改之前验证您的更改。

错误处理

  • 将 try/catch 块的主体提取到其自己的函数中,以便可以将错误处理与正常逻辑分开
  • 如果遵循“函数只做一件事情”的规则,则错误处理应在自己的函数中
  • 如果错误处理掩盖了逻辑,那么它是错误的

函数

  • 有一个单一的返回语句(不要提前返回)
  • 返回语句应在上面的内容之间有一个换行符,以清楚地定义函数现在已完成,并分离焦点
  • 避免返回 null,避免将 null 作为参数传递
  • 尽可能避免使用类方法,而是使用实例或静态方法。
  • 不要使用“选择器参数”或布尔值作为函数的参数,因为这违反了单一责任原则。如果函数根据布尔参数执行不同的操作,它实际上做了两件事,因此名称将是误导性的。相反,你应该将函数拆分成更小的函数,每个函数执行布尔标志的一个分支

适当的函数结构

函数应遵循1-2-3原则

  1. 代码的第一组(通过空行分隔)通常是样板代码或设置,其中我们分配稍后需要的变量
  2. 代码的第二组(通过空行分隔)是逻辑部分,我们在这里实际执行函数调用的功能
  3. 第三组(通过空行分隔)应该是最终的返回语句。函数应该只在末尾返回一次,以便于维护和可读性(待办事项:提供对此的研究)

显然,有些函数比这更长或更短,但不要害怕函数中的换行。我见过无数没有换行的30行函数,这在扫描或推理代码时非常难看。将相关的代码块分开可以使维护和阅读代码更容易。

def is_number_large(my_number, threshold = 100):
    """Returns true if a number is larger than a custom threshold."""
    number_as_int = int(my_number)
    threshold_as_int = int(threshold)

    if number_as_int > threshold_as_int:
        large_number = True
    else:
        large_number = False

    return large_number

元数据

  • 将所有实例变量放在文件顶部,不要与公共函数混合
  • “主”函数应在文件中首先声明,这样您就可以从上到下继续阅读,以便深入挖掘
  • 文件代码行数不应超过200行
  • 行长度不应超过120个字符
  • 尽可能使用尾随逗号,这样列表可以轻松扩展,同时保持下一次差异较小
  • 创建一个一致的词汇表,并在公司内部共享,例如名称(例如:fetch、get、retrieve all最终都意味着同一件事,您将在代码库中使用哪一个?)。如果您以某种方式做某事,那么就那样做。这遵循了最小意外原则
  • 不要打包列表或块打包,始终展开列表,使每个项目都单独一行。垂直阅读比水平阅读更容易
  • 使用对象字面量而不是复杂的if/else或switch/case语句
  • 尽可能使用隐式真语句(例如:if im_awesome is True: — 与 — if im_awesome:
  • 在执行字符串比较时,将比较的字符串的小写/大写和空白去掉
  • 除非有明确的原因不这样做,否则始终对列表进行排序。这确保了差异保持较小,并且排序后的列表更容易维护和查找信息
  • 正面比负面更容易理解,在if/else语句的情况下,让“if”部分是正面逻辑
  • 不要包含死代码。这可能是在if/else块中永远不会到达的代码,或者在try/except块中永远不会抛出的代码
  • 考虑“垂直分离”,变量和函数应在它们被使用的地方定义,而不是几百行之外
  • 通用静态函数不应包含在类中,因为它们通常实际上没有与类耦合
  • 追求简洁,精确!不要用浮点数表示货币(将其分解为“分”的整数),不要避免在并发中添加锁/事务管理,等等——“不要对你的决策精度懒惰”
  • 在命名事物时避免使用像filter这样的词,因为它可能意味着filter outfilter in

命名

  • 不要试图显得聪明,直接说出来(例如:变量和函数名称)
    • 缩写或想出聪明的名称只会导致下一个工程师代码扫描更加困难
  • 使用常量或变量来定义整数和其他不易识别的字符串(而不是直接作为参数传递而不声明它们是什么)。一个很好的代码片段示例,说明这很有用,是:而不是直接返回以下内容,你可以将其分配给一个描述性的变量,然后返回这个干净的变量:{k: cls._objects_to_ids(v) for k, v in six.iteritems(params)}。这也适用于多个和/或语句,将它们分配给变量以帮助描述意图。

开源

  • 项目应放置在顶层src文件夹中,以便项目配置和文档可以存在于项目文件夹之外。
  • 尽可能将语言的版本回溯到最古老的维护版本,直到最新的版本。取消对不再接收维护的语言版本(安全更新等)的支持,并尽早采用新版本。

测试

  • 干净的测试遵循五条规则
    • 快速:测试必须是快速的,否则你会停止运行它们,代码会变得陈旧。
    • 独立:测试不应该需要按照特定顺序运行。它们不应该为其他测试设置条件。
    • 可重复:测试应该在任何环境中可重复,并且每次都会产生相同的输出。
    • 自我验证:测试应该是通过/失败,你不需要检查日志或比较文件来查看测试是否通过。
    • 及时:测试应该在它们将要测试的生产代码编写之前不久编写。
  • 单元测试应遵循构建-操作-检查模型,其中测试数据被构建,然后操作函数,最后将结果与预期断言。不要向测试中添加额外的噪音。
  • 每个单元测试只测试一个概念。
  • 不要为了让自己感觉更好而模拟;只有当你必须这样做时才模拟。

语言特定

CSharp

工具

CSS

CSS 工具

CSS 样式

以下是一个清单,列出了每个网站都应该拥有的项目

  • 网站必须为使用的每个字体使用一个回退字体

Docker

Docker 工具

Golang

Golang 工具

HTML

HTML 工具

HTML 样式

  • 当你想要对某物进行间距处理时,不要反复使用&nbsp;,而是使用一个在<span>中的CSS类来这样做。
  • 避免使用内联CSS样式(使用CSS类和外部样式表)。
  • <br />标签应仅包含在<p>标签内。否则应使用CSS类提供垂直间距。
  • 始终为样式表和脚本列出href/src,以便易于阅读和视觉扫描(例如:<a href="https://example.com" target="_blank">example</a>)。
  • 样式表需要rel

以下是一个清单,列出了每个网站都应该拥有的项目

  • 网站必须有Favicon。
  • 当用户访问一个不存在的页面时,网站必须显示一个格式良好的404 页面
  • 网站必须适当地处理其他4xx5xx页面。
  • 网站必须使用Google Analytics或其他分析方法。
  • 网站必须使用模块化页面(例如:导航栏/页脚是其自己的组件,并可重复使用)
  • 网站必须有一个 meta author 标签
  • 网站必须有一个包含无重复内容的 meta keyword 标签
  • 网站必须有一个 meta description 标签
  • 网站必须在 html 标签中定义一个 language
  • 网站必须定义一个 meta 字符集(例如:UTF-8)
  • 网站必须定义一个 robots.txt 文件,该文件将所有公开页面列入白名单(可以使用通配符),并明确排除不应访问的页面
  • 网站必须在每个图像上定义 alt & title 标签
  • 网站必须有一个公开可用的 Sitemap

Java

Java 工具

JavaScript

JavaScript 工具

  • 格式化工具: Prettier
    • 此仓库中找到的配置文件:.prettierrc.yml
  • 检查器: ESLint
    • 此仓库中找到的配置文件:.eslintrc.yml
  • 测试: Mocha
  • VCR: Polly.js

PHP

PHP 工具

Python

Python 工具

  • 格式化工具: Black
    • 此仓库中找到的配置文件:pyproject.toml
  • 导入排序器: iSort
  • 检查器: Flake8
    • 此仓库中找到的配置文件:.flake8
  • 安全性: Bandit
  • 静态分析: mypy
  • 测试: Pytest
  • VCR: VCR.py

Python 风格

  • 在 Python 中难以轻松终止线程,在处理并发时请记住这一点
  • 不要定义原始路径,您必须使用 os.path.join() 函数,这样它将根据您所在的操作系统自动构建路径(例如:Windows 上的反斜杠)
  • 使用 datetime.timedelta 来偏移日期,它将根据需要自动滚动月份和年份。不要将 +6 添加到日期或年份,否则您将遇到错误,例如月份无法包含 35 天
  • "在它被使用的地方模拟它,而不是它来自的地方."

通常,执行如下操作是不良做法。如果在检查和删除文件之间文件被删除,将发生错误

# Anti-pattern
if os.path.isfile(file_path):
    os.remove(file_path)

# Use instead
try:
    os.remove(file_path)
except FileNotFoundError:
    pass

请确保错误断言与 pytest 上下文辅助函数缩进一致(这经常让我头疼)

# This will fail
with pytest.raises(Error) as error:
    my_function('BAD_INPUT')

    assert str(error.value) == 'You sent bad input'

# This will succeed
with pytest.raises(Error) as error:
    my_function('BAD_INPUT')

assert str(error.value) == 'You sent bad input'

Ruby

Ruby 工具

Shell(Bash)

Shell 工具

Shell 风格

  • 构建具有最大兼容性范围的 shell 脚本和工具,确保它们符合 POSIX 标准,不仅能在最新的 Bash 版本上运行,还能在 shdashksh 的最旧版本上运行

Swift

Swift 工具

网站与基础设施

网站与基础设施风格

  • 网站必须配置 www. 别名
  • 网站必须配置 SSL(例如:Let's Encrypt)
  • 网站必须通过 SSL 证书测试 并获得 A 或更好评级
  • 网站必须通过 速度测试 检查
  • 网站必须通过 兼容性测试 检查
  • 尽可能将 HTML、CSS 和 JS 压缩
  • 数据库应配置为至少有一个主要读写节点和多个只读副本。这有助于扩展并在必要时允许故障转移。应用程序应仅从副本读取并写入主节点。报告和分析可以从专用副本中提取,这样生产数据就不会受到影响。
  • 服务应进行负载均衡,至少为每个 "容器" 提供两个实例,以便在节点失败时提供高可用性
  • 服务应具有健康检查,在失败时终止,并在健康检查失败时自动重启,以实现自动恢复