1. 代码规范重要性与管理策略
🎯 为什么需要代码规范
- 提升代码可读性和可维护性
- 减少代码审查时间和争议
- 降低新人上手成本
- 预防常见错误和安全隐患
- 统一团队编码风格
📊 规范执行收益
- 代码审查效率提升 40%+
- Bug 率降低 30%+
- 技术债务减少 50%+
- 团队协作摩擦减少
- 代码复用率提高
🛠️ 工具体系
- 静态分析:ESLint/CheckStyle
- 格式化:Prettier/Spotless
- 提交规范:Commitlint/Husky
- 质量平台:SonarQube
- 依赖管理:pnpm/Dependabot
📈 实施策略
- 渐进式引入,避免一刀切
- 自动化优先,减少人工干预
- IDE 集成,实时反馈
- CI 卡点,强制合规
- 定期回顾,持续优化
💡 规范分级管理:
- Error 级别:必须修复,否则构建失败(如语法错误、安全问题)
- Warning 级别:建议修复,不影响构建(如代码风格问题)
- Info 级别:提示信息,供参考(如复杂度警告)
- 策略:新项目严格模式,老项目渐进式修复
2. 前端代码规范配置(ESLint)
2.1 ESLint 基础配置
# ===== package.json 依赖 =====
{
"devDependencies": {
"eslint": "^9.0.0",
"@eslint/js": "^9.0.0",
"typescript-eslint": "^7.0.0",
"eslint-plugin-react": "^7.34.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-testing-library": "^6.2.0",
"eslint-config-prettier": "^9.1.0"
}
}
# ===== eslint.config.js (Flat Config - ESLint 9+) =====
import js from '@eslint/js';
import tsParser from 'typescript-eslint';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import importPlugin from 'eslint-plugin-import';
import prettierConfig from 'eslint-config-prettier';
export default tsParser.config(
// 全局忽略文件
{
ignores: [
'**/node_modules/**',
'**/dist/**',
'**/build/**',
'**/coverage/**',
'**/*.min.js',
'**/vendor/**'
]
},
// JavaScript 基础规则
{
files: ['**/*.{js,jsx}'],
extends: [js.configs.recommended],
languageOptions: {
ecmaVersion: 2024,
sourceType: 'module',
globals: {
browser: 'readonly',
node: 'readonly',
process: 'readonly',
console: 'readonly',
Promise: 'readonly'
}
},
rules: {
// 可能的错误
'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
'no-console': ['warn', { allow: ['warn', 'error'] }],
'no-debugger': 'error',
// 最佳实践
'eqeqeq': ['error', 'always'],
'curly': ['error', 'all'],
'default-case': 'error',
'no-eval': 'error',
'no-implied-eval': 'error',
// 代码风格
'indent': ['error', 2],
'quotes': ['error', 'single', { avoidEscape: true }],
'semi': ['error', 'always'],
'comma-dangle': ['error', 'always-multiline']
}
},
// TypeScript 配置
{
files: ['**/*.{ts,tsx}'],
extends: [
...tsParser.configs.strictTypeChecked,
...tsParser.configs.stylisticTypeChecked
],
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname
}
},
rules: {
// TypeScript 特定规则
'@typescript-eslint/no-unused-vars': [
'warn',
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' }
],
'@typescript-eslint/explicit-function-return-type': [
'warn',
{ allowExpressions: true }
],
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/prefer-nullish-coalescing': 'error',
'@typescript-eslint/prefer-optional-chain': 'error',
'@typescript-eslint/strict-boolean-expressions': [
'warn',
{ allowString: false, allowNumber: false }
]
}
},
// React 配置
{
files: ['**/*.{jsx,tsx}'],
plugins: {
react,
'react-hooks': reactHooks
},
settings: {
react: { version: 'detect' }
},
rules: {
// React 最佳实践
'react/react-in-jsx-scope': 'off',
'react/prop-types': 'off', // 使用 TypeScript 代替
'react/jsx-no-target-blank': 'error',
'react/no-unescaped-entities': 'error',
// Hooks 规则
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn'
}
},
// 导入顺序规范
{
files: ['**/*.{js,jsx,ts,tsx}'],
plugins: { import: importPlugin },
rules: {
'import/order': [
'error',
{
groups: [
'builtin',
'external',
'internal',
['parent', 'sibling'],
'index',
'type'
],
'newlines-between': 'always',
alphabetize: { order: 'asc', caseInsensitive: true }
}
],
'import/no-duplicates': 'error',
'import/no-unresolved': 'error'
}
},
// 测试文件特殊规则
{
files: ['**/*.test.{ts,tsx}', '**/*.spec.{ts,tsx}'],
extends: ['plugin:testing-library/react'],
rules: {
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off'
}
},
// Prettier 冲突解决(放在最后)
prettierConfig
);
2.2 共享 ESLint 配置包(Monorepo)
# ===== packages/eslint-config/package.json =====
{
"name": "@company/eslint-config",
"version": "1.0.0",
"type": "module",
"exports": {
"./base": "./base.js",
"./react": "./react.js",
"./typescript": "./typescript.js",
"./prettier": "./prettier.js"
},
"peerDependencies": {
"eslint": "^9.0.0",
"typescript": "^5.0.0"
},
"dependencies": {
"@eslint/js": "^9.0.0",
"typescript-eslint": "^7.0.0",
"eslint-plugin-react": "^7.34.0",
"eslint-config-prettier": "^9.1.0"
}
}
# ===== packages/eslint-config/react.js =====
import baseConfig from './base.js';
import react from 'eslint-plugin-react';
export default [
...baseConfig,
{
files: ['**/*.{jsx,tsx}'],
plugins: { react },
settings: { react: { version: 'detect' } },
rules: {
'react/react-in-jsx-scope': 'off',
'react/prop-types': 'off',
'react/jsx-no-target-blank': 'error',
'react/self-closing-comp': 'error',
'react/jsx-sort-props': ['warn', {
callbacksLast: true,
shorthandFirst: true,
reservedFirst: true
}]
}
}
];
# ===== 在项目中使用共享配置 =====
// apps/web-admin/eslint.config.js
import companyConfig from '@company/eslint-config/react';
export default [
...companyConfig,
{
rules: {
// 项目特定覆盖
'no-console': 'error',
'@typescript-eslint/no-explicit-any': 'error'
}
}
];
3. 代码格式化配置(Prettier)
3.1 Prettier 基础配置
# ===== .prettierrc / prettier.config.js =====
export default {
// 每行最大字符数
printWidth: 100,
// 缩进空格数
tabWidth: 2,
// 使用空格而非 Tab
useTabs: false,
// 语句末尾加分号
semi: true,
// 使用单引号
singleQuote: true,
// 对象属性引号(仅在需要时添加)
quoteProps: 'as-needed',
// JSX 中使用单引号
jsxSingleQuote: false,
// 尾随逗号(多行时添加)
trailingComma: 'es5',
// 对象字面量括号间加空格
bracketSpacing: true,
// JSX 标签的 > 不另起一行
bracketSameLine: false,
// 箭头函数单个参数不加括号
arrowParens: 'avoid',
// 每个文件格式化的范围是整个文件
rangeStart: 0,
rangeEnd: Infinity,
// 不需要 @prettier 注释即可格式化
requirePragma: false,
// 不在文件顶部插入 @format 注释
insertPragma: false,
// Markdown 文本换行
proseWrap: 'preserve',
// HTML 空格敏感性
htmlWhitespaceSensitivity: 'css',
// Vue 文件中 <script> 和 <style> 标签内的代码缩进
vueIndentScriptAndStyle: false,
// 换行符(根据系统自动检测)
endOfLine: 'lf',
// 格式化嵌入的代码(如 Markdown 中的代码块)
embeddedLanguageFormatting: 'auto',
// 单引号 HTML 属性
singleAttributePerLine: false
};
# ===== .prettierignore =====
# 依赖目录
node_modules/
.pnp/
# 构建输出
dist/
build/
out/
.next/
.nuxt/
# 缓存文件
.cache/
.parcel-cache/
# 覆盖率报告
coverage/
# 锁定文件
package-lock.json
yarn.lock
pnpm-lock.yaml
# 编译产物
**/*.min.js
**/*.bundle.js
# 第三方代码
vendor/
public/vendor/
# 配置文件(已格式化的除外)
CHANGELOG.md
3.2 ESLint 与 Prettier 集成
# ===== 方案 1:使用 eslint-config-prettier(推荐)=====
// eslint.config.js
import prettierConfig from 'eslint-config-prettier';
export default [
// ... 其他配置
// 必须放在最后,禁用所有与 Prettier 冲突的规则
prettierConfig
];
# ===== 方案 2:使用 eslint-plugin-prettier =====
// 将 Prettier 作为 ESLint 规则运行
import prettierPlugin from 'eslint-plugin-prettier';
export default [
{
plugins: { prettier: prettierPlugin },
rules: {
'prettier/prettier': 'error'
}
}
];
# ===== VS Code 设置(.vscode/settings.json)=====
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
3.3 Husky + lint-staged 自动化
# ===== 安装依赖 =====
npm install --save-dev husky lint-staged
npx husky init
# ===== package.json 配置 =====
{
"scripts": {
"prepare": "husky",
"lint": "eslint . --ext .ts,.tsx,.js,.jsx",
"lint:fix": "eslint . --ext .ts,.tsx,.js,.jsx --fix",
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
"format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md}\""
},
"lint-staged": {
"*.{ts,tsx,js,jsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,md,yml,yaml}": [
"prettier --write"
]
}
}
# ===== .husky/pre-commit =====
#!/usr/bin/env sh
. "/usr/local/share/nvm/nvm.sh"
nvm use default
npx lint-staged
# ===== .husky/commit-msg =====
#!/usr/bin/env sh
. "/usr/local/share/nvm/nvm.sh"
nvm use default
npx commitlint --edit $1
4. 后端代码规范配置(CheckStyle)
4.1 Maven 项目 CheckStyle 配置
# ===== pom.xml 配置 =====
<project>
<build>
<plugins>
<!-- CheckStyle 插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<configLocation>checkstyle/checkstyle.xml</configLocation>
<encoding>UTF-8</encoding>
<consoleOutput>true</consoleOutput>
<failsOnError>true</failsOnError>
<failOnViolation>true</failOnViolation>
<violationSeverity>warning</violationSeverity>
<includeTestSourceDirectory>true</includeTestSourceDirectory>
<linkXRef>false</linkXRef>
</configuration>
<executions>
<execution>
<id>validate</id>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>10.14.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
# ===== checkstyle/checkstyle.xml =====
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
<property name="charset" value="UTF-8"/>
<property name="severity" value="warning"/>
<property name="fileExtensions" value="java, properties, xml"/>
<!-- 文件头检查 -->
<module name="RegexpHeader">
<property name="headerFile" value="${checkstyle.header.file}"/>
<property name="fileExtensions" value="java"/>
</module>
<!-- 文件大小检查 -->
<module name="FileLength">
<property name="max" value="2000"/>
</module>
<!-- 行长度检查 -->
<module name="LineLength">
<property name="max" value="120"/>
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
</module>
<!-- 禁止 Tab -->
<module name="FileTabCharacter">
<property name="eachLine" value="true"/>
</module>
<!-- 文件末尾换行 -->
<module name="NewlineAtEndOfFile"/>
<module name="TreeWalker">
<!-- 代码规范 -->
<module name="OuterTypeFilename"/>
<module name="IllegalTokenText">
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
<property name="format" value="\\\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\\\(0(10|11|12|14|15|42|47)|134)">
<property name="message" value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
</module>
<!-- 避免转义字符 -->
<module name="AvoidEscapedUnicodeCharacters">
<property name="allowEscapesForControlCharacters" value="true"/>
<property name="allowByTailComment" value="true"/>
<property name="allowNonPrintableEscapes" value="true"/>
</module>
<!-- 行尾通配符导入 -->
<module name="AvoidStarImport"/>
<!-- 类导入顺序 -->
<module name="CustomImportOrder">
<property name="sortImportsInGroupAlphabetically" value="true"/>
<property name="separateLineBetweenGroups" value="true"/>
<property name="customImportOrderRules" value="STATIC###THIRD_PARTY_PACKAGE"/>
</module>
<!-- 方法参数数量限制 -->
<module name="ParameterNumber">
<property name="max" value="8"/>
<property name="tokens" value="METHOD_DEF"/>
</module>
<!-- 方法复杂度限制 -->
<module name="CyclomaticComplexity">
<property name="max" value="15"/>
</module>
<!-- 嵌套深度限制 -->
<module name="NestedForDepth">
<property name="max" value="2"/>
</module>
<module name="NestedIfDepth">
<property name="max" value="3"/>
</module>
<!-- 命名规范 -->
<module name="PackageName">
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
</module>
<module name="TypeName">
<property name="format" value="^[A-Z][a-zA-Z0-9]*$"/>
</module>
<module name="MethodName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
</module>
<module name="ParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
</module>
<module name="LambdaParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
</module>
<module name="CatchParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
</module>
<module name="LocalVariableName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
</module>
<module name="ClassTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
</module>
<module name="MethodTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
</module>
<!-- 空代码块检查 -->
<module name="EmptyBlock">
<property name="option" value="TEXT"/>
<property name="tokens" value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
</module>
<!-- 右花括号位置 -->
<module name="RightCurly">
<property name="id" value="RightCurlySame"/>
<property name="tokens" value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE"/>
</module>
<module name="RightCurly">
<property name="id" value="RightCurlyAlone"/>
<property name="option" value="alone"/>
<property name="tokens" value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT, INSTANCE_INIT"/>
</module>
<!-- 空白行规范 -->
<module name="WhitespaceAfter">
<property name="tokens" value="COMMA, SEMI, TYPECAST"/>
</module>
<module name="NoWhitespaceBefore">
<property name="tokens" value="COMMA, SEMI, POST_INC, POST_DEC, DOT"/>
</module>
<!-- 操作符换行 -->
<module name="OperatorWrap">
<property name="option" value="NL"/>
<property name="tokens" value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR, LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF"/>
</module>
<!-- 注解位置 -->
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationMostCases"/>
<property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
</module>
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationVariables"/>
<property name="tokens" value="VARIABLE_DEF"/>
<property name="allowSamelineMultipleAnnotations" value="true"/>
</module>
<!-- Javadoc 规范 -->
<module name="MissingJavadocMethod">
<property name="scope" value="public"/>
<property name="minLineCount" value="2"/>
<property name="allowedAnnotations" value="Override, Test"/>
</module>
<module name="MissingJavadocType">
<property name="scope" value="protected"/>
<property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, RECORD_DEF, ANNOTATION_DEF"/>
<property name="excludeScope" value="nothing"/>
</module>
</module>
</module>
4.2 Spotless 代码格式化(Gradle)
# ===== build.gradle 配置 =====
plugins {
id 'java'
id 'com.diffplug.spotless' version '6.25.0'
}
spotless {
java {
// 使用 Google Java Format
googleJavaFormat('1.22.0')
// 移除未使用的 import
removeUnusedImports()
// 格式化 import 顺序
importOrder('java', 'javax', 'org', 'com')
// 许可证头
licenseHeaderFile rootProject.file('spotless/license.java')
// 排除文件
targetExclude '**/generated/**'
}
format 'misc' {
target '**/*.md', '**/*.gradle', '**/*.gitignore'
trimTrailingWhitespace()
leadingTabsToSpaces(2)
endWithNewline()
}
}
// 在构建前执行格式化检查
tasks.named('build') {
dependsOn tasks.named('spotlessCheck')
}
# ===== spotless/license.java =====
/*
* Copyright © ${YEAR} Your Company. All rights reserved.
* Licensed under the Apache License, Version 2.0
*/
5. SonarQube 代码质量平台
5.1 SonarQube 配置
# ===== sonar-project.properties (前端) =====
# 项目基本信息
sonar.projectKey=myapp-frontend
sonar.projectName=MyApp Frontend
sonar.projectVersion=1.0.0
# 源代码位置
sonar.sources=src
sonar.tests=tests
sonar.exclusions=**/node_modules/**,**/dist/**,**/build/**,**/coverage/**
# TypeScript 配置
sonar.language=ts,tsx
sonar.typescript.tsconfigPath=tsconfig.json
# 测试覆盖率
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.coverage.exclusions=**/*.test.ts,**/*.spec.ts,**/__tests__/**,**/mocks/**
# 代码重复检测
sonar.cpd.exclusions=**/*.test.ts,**/*.spec.ts
# 质量问题门禁
sonar.qualitygate.wait=true
sonar.qualitygate.timeout=300
# ===== pom.xml 配置(后端 Java)=====
<properties>
<sonar.host.url>http://sonarqube.yourcompany.com:9000</sonar.host.url>
<sonar.login>${SONAR_TOKEN}</sonar.login>
<sonar.projectKey>myapp-backend</sonar.projectKey>
<sonar.projectName>MyApp Backend</sonar.projectName>
<sonar.sources>src/main/java</sonar.sources>
<sonar.tests>src/test/java</sonar.tests>
<sonar.java.binaries>target/classes</sonar.java.binaries>
<sonar.junit.reportsPath>target/surefire-reports</sonar.junit.reportsPath>
<sonar.jacoco.reportPath>target/jacoco.exec</sonar.jacoco.reportPath>
<sonar.coverage.exclusions>**/test/**,**/*Test.java</sonar.coverage.exclusions>
</properties>
<build>
<plugins>
<!-- JaCoCo 代码覆盖率 -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.11</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
# ===== Jenkins Pipeline 集成 =====
stage('SonarQube Analysis') {
steps {
withSonarQubeEnv('sonarqube-prod') {
sh '''
mvn clean verify sonar:sonar \\
-Dsonar.projectKey=${PROJECT_KEY} \\
-Dsonar.host.url=${SONAR_HOST_URL} \\
-Dsonar.login=${SONAR_TOKEN}
'''
}
}
}
stage('Quality Gate') {
steps {
timeout(time: 1, unit: 'HOURS') {
waitForQualityGate abortPipeline: true
}
}
}
5.2 质量门禁标准
| 指标 | 新代码 | 整体代码 | 严重级别 |
|---|---|---|---|
| Bugs | 0 | < 10 | 🔴 Error |
| Vulnerabilities | 0 | 0 | 🔴 Error |
| Security Hotspots | Reviewed | < 5 | 🟡 Warning |
| Code Smells | < 15 | < 200 | 🟡 Warning |
| 测试覆盖率 | > 80% | > 70% | 🟡 Warning |
| 重复代码 | < 3% | < 5% | 🟡 Warning |
| 认知复杂度 | < 15/方法 | - | 🔵 Info |
| 技术债务比率 | < 5% | < 10% | 🟡 Warning |
6. Git 提交信息规范(Commitlint)
6.1 Commitlint 配置
# ===== 安装依赖 =====
npm install --save-dev @commitlint/cli @commitlint/config-conventional
# ===== commitlint.config.js =====
export default {
extends: ['@commitlint/config-conventional'],
rules: {
// type 枚举值
'type-enum': [
2,
'always',
[
'feat', // 新功能
'fix', // Bug 修复
'docs', // 文档更新
'style', // 代码样式(不影响功能)
'refactor', // 重构
'perf', // 性能优化
'test', // 测试用例
'chore', // 构建/工具变动
'revert', // 回退
'merge', // 合并分支
'ci', // CI 配置
'build' // 构建系统
]
],
// type 大小写
'type-case': [2, 'always', 'lower-case'],
// type 不能为空
'type-empty': [2, 'never'],
// scope 不能为空(可选)
'scope-empty': [1, 'never'],
// scope 枚举值
'scope-enum': [
1,
'always',
[
'auth', 'user', 'order', 'payment',
'api', 'ui', 'db', 'config',
'deps', 'release'
]
],
// subject 大小写
'subject-case': [
2,
'never',
['sentence-case', 'start-case', 'pascal-case', 'upper-case']
],
// subject 不能为空
'subject-empty': [2, 'never'],
// subject 结尾无句号
'subject-full-stop': [2, 'never', '.'],
// header 最大长度
'header-max-length': [2, 'always', 100],
// body 最大长度
'body-max-line-length': [2, 'always', 100],
// footer 前缀
'footer-leading-blank': [2, 'always'],
'footer-max-line-length': [2, 'always', 100]
},
// 提示消息
prompt: {
questions: {
type: {
description: '选择变更类型',
enum: {
feat: {
description: '新功能',
title: 'Features',
emoji: '✨'
},
fix: {
description: 'Bug 修复',
title: 'Bug Fixes',
emoji: '🐛'
},
docs: {
description: '文档更新',
title: 'Documentation',
emoji: '📝'
},
style: {
description: '代码样式',
title: 'Styles',
emoji: '💄'
},
refactor: {
description: '代码重构',
title: 'Code Refactoring',
emoji: '♻️'
},
perf: {
description: '性能优化',
title: 'Performance Improvements',
emoji: '⚡️'
},
test: {
description: '测试用例',
title: 'Tests',
emoji: '✅'
},
chore: {
description: '其他改动',
title: 'Chores',
emoji: '🔧'
}
}
}
}
}
};
# ===== 提交示例 =====
# ✅ 正确的提交信息
feat(auth): add JWT token authentication
fix(user): resolve login timeout issue
docs(api): update REST API documentation
refactor(order): simplify order processing logic
perf(db): optimize database query performance
test(payment): add unit tests for payment service
chore(deps): upgrade React to v18.3.0
ci(jenkins): add deployment pipeline configuration
# ❌ 错误的提交信息
update code # type 缺失
FIX: bug fix # type 应小写
feat:no space after colon # 冒号后需空格
feat(auth):add feature. # subject 不应以句号结尾
6.2 Commitizen 交互式提交
# ===== 安装依赖 =====
npm install --save-dev commitizen cz-conventional-changelog
# ===== package.json 配置 =====
{
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
},
"scripts": {
"commit": "cz"
}
}
# ===== 使用方式 =====
npm run commit
# 或
npx cz
# 交互式问答:
? Select the type of change you are committing: (Use arrow keys)
❯ feat: A new feature
fix: A bug fix
docs: Documentation only changes
style: Changes that do not affect the meaning of the code
refactor: A code change that neither fixes a bug nor adds a feature
perf: A code change that improves performance
test: Adding missing tests or correcting existing tests
chore: Changes to the build process or auxiliary tools
? What is the scope of this change (e.g. component or file name): auth
? Write a short, imperative tense description of the change:
add JWT token authentication with refresh mechanism
? Provide a longer description of the change: (press enter to skip)
Implemented stateless JWT authentication with:
- Access token (15 minutes expiry)
- Refresh token (7 days expiry)
- Automatic token rotation
? Are there any breaking changes? No
? Does this change affect any open issues? Yes
? Add issue references (e.g. "fix #123", "re #123".): fix #100, fix #102
7. 依赖管理方案(pnpm/npm/yarn)
7.1 pnpm 工作空间配置(推荐)
# ===== 根目录 package.json =====
{
"name": "enterprise-monorepo",
"private": true,
"packageManager": "pnpm@9.0.0",
"scripts": {
"install:all": "pnpm install --recursive",
"build:all": "pnpm run -r build",
"test:all": "pnpm run -r test",
"lint:all": "pnpm run -r lint",
"clean": "pnpm clean -r && rm -rf node_modules"
},
"devDependencies": {
"typescript": "^5.4.0",
"eslint": "^9.0.0",
"prettier": "^3.2.0",
"vitest": "^1.3.0"
}
}
# ===== pnpm-workspace.yaml =====
packages:
# 所有包
- 'packages/*'
# 所有应用
- 'apps/*'
# 所有服务
- 'services/*'
# 排除特定目录
- '!**/test/**'
# ===== .npmrc 配置 =====
# 使用硬链接,节省磁盘空间
shamefully-hoist=false
# 严格对等模式,避免幽灵依赖
strict-peer-dependencies=true
# 自动安装 peer dependencies
auto-install-peers=true
# 使用国内镜像(可选)
registry=https://registry.npmmirror.com/
# 仅当 lockfile 存在时安装
prefer-offline=true
# 生产环境不安装 devDependencies
# production=true
# 忽略 scripts 脚本(安全考虑)
# ignore-scripts=true
# ===== 子项目 package.json =====
{
"name": "@company/ui-components",
"version": "1.0.0",
"dependencies": {
"react": "^18.3.0",
"react-dom": "^18.3.0"
},
"devDependencies": {
"@company/eslint-config": "workspace:*",
"@types/react": "^18.3.0",
"typescript": "^5.4.0"
},
"peerDependencies": {
"react": "^18.0.0"
}
}
7.2 依赖版本管理策略
# ===== 版本号语义(SemVer)=====
^1.2.3 = >=1.2.3 < 2.0.0 # 允许 minor 和 patch 更新
~1.2.3 = >=1.2.3 <1.3.0 # 仅允许 patch 更新
1.2.3 = 1.2.3 # 精确版本
* = >=0.0.0 # 任意版本
# ===== 推荐策略 =====
生产依赖: 使用 ^ 符号(允许向后兼容的更新)
开发依赖: 使用 ^ 符号或精确版本
框架核心: 使用 ~ 符号(如 React/Vue,避免 major 升级)
内部包: 使用 workspace: 协议
# ===== 依赖分类管理 =====
{
"dependencies": {
# 运行时必需的依赖
"react": "^18.3.0",
"axios": "^1.6.0"
},
"devDependencies": {
# 开发和构建时使用的依赖
"typescript": "^5.4.0",
"vite": "^5.2.0"
},
"peerDependencies": {
# 宿主环境应提供的依赖(库项目使用)
"react": "^18.0.0"
},
"optionalDependencies": {
# 可选依赖,安装失败不影响整体
"fsevents": "^2.3.0"
},
"resolutions": {
# 强制解析特定版本(解决依赖冲突)
"**/lodash": "^4.17.21"
}
}
# ===== 依赖更新命令 =====
# 查看可更新的依赖
pnpm outdated
npm outdated
yarn outdated
# 更新所有依赖到最新版本
pnpm update -r
npm update
yarn upgrade
# 交互式更新(推荐)
npx npm-check-updates -i
pnpm up -i
# 更新特定依赖
pnpm update react react-dom
npm update react
# 更新到最新 major 版本
ncu -u
pnpm update -r --latest
8. 依赖安全与漏洞扫描
8.1 安全扫描工具
# ===== npm audit(内置)=====
# 检查漏洞
npm audit
# 自动修复
npm audit fix
# 强制修复(可能破坏性)
npm audit fix --force
# 生成详细报告
npm audit --json > audit-report.json
# ===== pnpm audit =====
pnpm audit
pnpm audit fix
# ===== Snyk(第三方)=====
npm install -g snyk
snyk auth
snyk test
snyk monitor
snyk wizard # 交互式修复
# ===== Dependabot(GitHub 自动 PR)=====
# .github/dependabot.yml
version: 2
updates:
# npm 依赖更新
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "09:00"
timezone: "Asia/Shanghai"
open-pull-requests-limit: 10
reviewers:
- "security-team"
labels:
- "dependencies"
- "security"
commit-message:
prefix: "chore(deps)"
groups:
production-dependencies:
patterns:
- "*"
exclude-patterns:
- "*@dev*"
- "*@types*"
# GitHub Actions 更新
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
# ===== Renovate(高级依赖管理)=====
# renovate.json
{
"extends": [
"config:recommended",
":semanticCommits",
":automergeMinor"
],
"schedule": ["every weekend"],
"prHourlyLimit": 2,
"prConcurrentLimit": 10,
"labels": ["dependencies"],
"reviewers": ["team:frontend"],
"packageRules": [
{
"matchUpdateTypes": ["minor", "patch"],
"matchCurrentVersion": "!/^0/",
"automerge": true
},
{
"matchDepTypes": ["devDependencies"],
"automerge": true
},
{
"matchPackagePatterns": ["^@company/"],
"enabled": false
}
]
}
8.2 CI/CD 安全检查
# ===== Jenkins Pipeline 集成 =====
stage('Security Scan') {
steps {
script {
// npm audit
sh '''
cd apps/web-admin
npm audit --audit-level=high
'''
// Snyk 扫描
sh '''
snyk test --severity-threshold=high || true
'''
}
}
post {
failure {
echo '❌ Security vulnerabilities detected!'
slackSend(color: 'danger', message: "Security scan failed: ${BUILD_URL}")
}
}
}
# ===== GitHub Actions =====
name: Security Scan
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
schedule:
- cron: '0 2 * * 1' # 每周一凌晨 2 点
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run npm audit
run: pnpm audit --audit-level=high
- name: Run Snyk
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
severity-threshold: high
- name: Upload SARIF report
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: snyk.sarif
9. CI/CD 集成自动化检查
9.1 完整 CI/CD 流水线
pipeline {
agent any
options {
buildDiscarder(logRotator(numToKeepStr: '20'))
timeout(time: 1, unit: 'HOURS')
timestamps()
disableConcurrentBuilds()
}
environment {
NODE_VERSION = '20'
JAVA_VERSION = '21'
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Install Dependencies') {
steps {
sh 'pnpm install --frozen-lockfile'
}
}
stage('Code Quality Checks') {
parallel {
stage('ESLint') {
steps {
sh 'pnpm run lint:all'
}
}
stage('Prettier') {
steps {
sh 'pnpm run format:check'
}
}
stage('CheckStyle') {
steps {
sh 'mvn checkstyle:check'
}
}
stage('Type Check') {
steps {
sh 'pnpm run -r type-check'
}
}
}
}
stage('Unit Tests') {
steps {
sh 'pnpm run test:all --coverage'
}
post {
always {
junit allowEmptyResults: true, testResults: '**/junit-reports/*.xml'
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'coverage',
reportFiles: 'lcov-report/index.html',
reportName: 'Coverage Report'
])
}
}
}
stage('Build') {
steps {
sh 'pnpm run build:all'
sh 'mvn clean package -DskipTests'
}
}
stage('Security Scan') {
steps {
sh 'pnpm audit --audit-level=high || true'
sh 'snyk test --severity-threshold=high || true'
}
}
stage('SonarQube Analysis') {
steps {
withSonarQubeEnv('sonarqube-prod') {
sh 'mvn sonar:sonar'
}
}
}
stage('Quality Gate') {
steps {
timeout(time: 30, unit: 'MINUTES') {
waitForQualityGate abortPipeline: true
}
}
}
stage('Build Docker Image') {
steps {
script {
def imageName = "harbor.yourcompany.com/app/backend:${BUILD_NUMBER}"
sh """
docker build -t ${imageName} .
docker push ${imageName}
"""
}
}
}
}
post {
success {
echo '✅ All checks passed!'
slackSend(color: 'good', message: "Build #${BUILD_NUMBER} succeeded!")
}
failure {
echo '❌ Build failed!'
slackSend(color: 'danger', message: "Build #${BUILD_NUMBER} failed!")
}
unstable {
echo '⚠️ Build is unstable!'
}
}
}
10. 最佳实践与工具链整合
🎯 实施路线图
- 第 1 周:基础工具安装配置
- 第 2 周:IDE 集成与团队培训
- 第 3 周:CI/CD 卡点实施
- 第 4 周:遗留代码渐进修复
- 持续:定期回顾优化
📊 度量指标
- 代码规范违规数趋势
- 构建成功率与时长
- 代码覆盖率变化
- 技术债务比率
- 安全漏洞数量
🛠️ 工具链清单
- ESLint 9.x + Prettier 3.x
- CheckStyle 10.x + Spotless
- SonarQube 10.x
- Commitlint + Husky
- pnpm 9.x + Snyk
✅ 成功要素
- 管理层支持与推动
- 全员参与和认同
- 自动化优先原则
- 渐进式改进策略
- 持续优化文化
🎉 代码规范与依赖管理最佳实践总结:
- 工具先行:用自动化工具替代人工检查
- IDE 集成:实时反馈,问题早发现
- CI 卡点:不合格代码无法合并
- 渐进改进:新项目严格,老项目逐步修复
- 安全第一:依赖漏洞零容忍
- 文档同步:规范变更及时更新文档
- 定期审计:每季度审查规范和依赖