justintime50 / styles
用于我的项目和团队的风格指南和最佳实践的集合。
0.6.0
2024-05-31 20:24 UTC
README
用于我的项目和团队的风格指南和最佳实践的集合。
这份文档将不断更新,在接下来的几个月内会有很多变化,因为我将收集更多信息来添加这里。当我完成时,我希望最终产品看起来像这样:[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原则
- 代码的第一组(通过空行分隔)通常是样板代码或设置,其中我们分配稍后需要的变量
- 代码的第二组(通过空行分隔)是逻辑部分,我们在这里实际执行函数调用的功能
- 第三组(通过空行分隔)应该是最终的返回语句。函数应该只在末尾返回一次,以便于维护和可读性(待办事项:提供对此的研究)
显然,有些函数比这更长或更短,但不要害怕函数中的换行。我见过无数没有换行的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 out
或filter in
命名
- 不要试图显得聪明,直接说出来(例如:变量和函数名称)
- 缩写或想出聪明的名称只会导致下一个工程师代码扫描更加困难
- 使用常量或变量来定义整数和其他不易识别的字符串(而不是直接作为参数传递而不声明它们是什么)。一个很好的代码片段示例,说明这很有用,是:而不是直接返回以下内容,你可以将其分配给一个描述性的变量,然后返回这个干净的变量:
{k: cls._objects_to_ids(v) for k, v in six.iteritems(params)}
。这也适用于多个和/或语句,将它们分配给变量以帮助描述意图。
开源
- 项目应放置在顶层
src
文件夹中,以便项目配置和文档可以存在于项目文件夹之外。 - 尽可能将语言的版本回溯到最古老的维护版本,直到最新的版本。取消对不再接收维护的语言版本(安全更新等)的支持,并尽早采用新版本。
测试
- 干净的测试遵循五条规则
- 快速:测试必须是快速的,否则你会停止运行它们,代码会变得陈旧。
- 独立:测试不应该需要按照特定顺序运行。它们不应该为其他测试设置条件。
- 可重复:测试应该在任何环境中可重复,并且每次都会产生相同的输出。
- 自我验证:测试应该是通过/失败,你不需要检查日志或比较文件来查看测试是否通过。
- 及时:测试应该在它们将要测试的生产代码编写之前不久编写。
- 单元测试应遵循构建-操作-检查模型,其中测试数据被构建,然后操作函数,最后将结果与预期断言。不要向测试中添加额外的噪音。
- 每个单元测试只测试一个概念。
- 不要为了让自己感觉更好而模拟;只有当你必须这样做时才模拟。
语言特定
CSharp
工具
- 代码检查器: Dotnet format
- VCR: EasyVCR
CSS
CSS 工具
- 代码检查器: stylelint
CSS 样式
以下是一个清单,列出了每个网站都应该拥有的项目
- 网站必须为使用的每个字体使用一个
回退字体
。
Docker
Docker 工具
- 代码检查器: Hadolint
Golang
Golang 工具
- 格式化器: Gofmt
- 代码检查器: Golangci-lint
- 安全性: Gosec
- VCR: go-vcr
HTML
HTML 工具
- 代码检查器: HTMLHint
HTML 样式
- 当你想要对某物进行间距处理时,不要反复使用
,而是使用一个在<span>
中的CSS类来这样做。 - 避免使用内联CSS样式(使用CSS类和外部样式表)。
<br />
标签应仅包含在<p>
标签内。否则应使用CSS类提供垂直间距。- 始终为样式表和脚本列出
href/src
,以便易于阅读和视觉扫描(例如:<a href="https://example.com" target="_blank">example</a>
)。 - 样式表需要
rel
。
以下是一个清单,列出了每个网站都应该拥有的项目
- 网站必须有Favicon。
- 当用户访问一个不存在的页面时,网站必须显示一个格式良好的
404 页面
。 - 网站必须适当地处理其他
4xx
和5xx
页面。 - 网站必须使用
Google Analytics
或其他分析方法。 - 网站必须使用模块化页面(例如:导航栏/页脚是其自己的组件,并可重复使用)
- 网站必须有一个
meta author 标签
- 网站必须有一个包含无重复内容的
meta keyword 标签
- 网站必须有一个
meta description 标签
- 网站必须在
html
标签中定义一个language
- 网站必须定义一个
meta 字符集
(例如:UTF-8) - 网站必须定义一个
robots.txt
文件,该文件将所有公开页面列入白名单(可以使用通配符),并明确排除不应访问的页面 - 网站必须在每个图像上定义
alt & title 标签
- 网站必须有一个公开可用的
Sitemap
Java
Java 工具
- 检查器: Checkstyle
- 静态分析: Error Prone
- VCR: EasyVCR
JavaScript
JavaScript 工具
- 格式化工具: Prettier
- 此仓库中找到的配置文件:
.prettierrc.yml
- 此仓库中找到的配置文件:
- 检查器: ESLint
- 此仓库中找到的配置文件:
.eslintrc.yml
- 此仓库中找到的配置文件:
- 测试: Mocha
- VCR: Polly.js
PHP
PHP 工具
- 检查器: PHP_CodeSniffer
- 测试: PHPUnit
- VCR: PHP VCR
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 工具
- 代码检查工具: ShellCheck
Shell 风格
- 构建具有最大兼容性范围的 shell 脚本和工具,确保它们符合 POSIX 标准,不仅能在最新的 Bash 版本上运行,还能在
sh
、dash
或ksh
的最旧版本上运行
Swift
Swift 工具
- 代码检查工具: Swiftlint
网站与基础设施
网站与基础设施风格
- 网站必须配置
www.
别名 - 网站必须配置
SSL
(例如:Let's Encrypt) - 网站必须通过
SSL 证书测试
并获得A
或更好评级 - 网站必须通过
速度测试
检查 - 网站必须通过
兼容性测试
检查 - 尽可能将 HTML、CSS 和 JS 压缩
- 数据库应配置为至少有一个主要读写节点和多个只读副本。这有助于扩展并在必要时允许故障转移。应用程序应仅从副本读取并写入主节点。报告和分析可以从专用副本中提取,这样生产数据就不会受到影响。
- 服务应进行负载均衡,至少为每个 "容器" 提供两个实例,以便在节点失败时提供高可用性
- 服务应具有健康检查,在失败时终止,并在健康检查失败时自动重启,以实现自动恢复