09项目命名与版本管理

为文件、目录、变量和函数建立一致命名,并用 Git、日期与发布版本分别管理代码历史、数据快照和软件发布。

2025-04-30
RNamingGitVersion ControlProject Management
本章目录 · 9

分析项目变乱,通常不是因为文件太多,而是名字没有表达用途、多个副本争当“最终版”,以及代码历史、数据批次和软件版本被混成了同一件事。

final.R
final_v2.R
final_v2_new.R
final_v2_new_last.R

解决方法不是设计更复杂的文件后缀,而是把职责分开:名称说明内容,目录说明角色,Git 保存代码历史,日期标识数据或结果快照,版本号标识正式发布。

文件名说明内容

文件名应当稳定、可预测,并能在脱离原目录后仍提供基本信息。R 项目中可以采用小写 snake_case:

sample_metadata.csv
clean_expression_data.R
fit_logistic_models.R

同一项目内保持一种分隔风格,比争论下划线还是连字符更重要。实用规则包括:

  • 使用能说明内容或动作的词,避免 newtemp2final
  • 不依赖空格、大小写差异或特殊符号区分文件;
  • 扩展名保持准确,不把格式信息重复写进主体名称;
  • 批处理脚本需要固定顺序时,使用 01_02_ 等零填充前缀。
01_import_data.R
02_clean_data.R
03_fit_models.R
04_make_figures.R

数字前缀表达执行顺序,不是版本号。如果脚本之间已经由工作流工具显式管理依赖,就不必再靠编号维持顺序。

日期只标识真正的时间快照

日期适合区分来自不同时间点、之后不会原地修改的数据或结果:

sample_manifest_2025-04-30.csv
database_snapshot_2025-04-30.parquet
report_2025-04-30.pdf

推荐使用 YYYY-MM-DD,这样按文件名排序就是时间顺序。日期不适合代替代码版本管理;每天修改同一脚本时,不应不断复制出 analysis_2025-04-29.Ranalysis_2025-04-30.R

文件名中的日期还应说明它代表什么:是数据采集日、下载日、分析运行日还是报告发布日期。若这个含义并不明显,应写进 README、数据字典或元数据中。

目录说明文件角色

一个简单的分析项目可以按职责分层:

project/
├─ data/
│  ├─ raw/
│  └─ processed/
├─ R/
├─ results/
│  ├─ figures/
│  └─ tables/
├─ README.md
└─ project.Rproj
  • data/raw/ 保存原始输入,通常不原地修改;
  • data/processed/ 保存可由代码重新生成的中间数据;
  • R/ 保存函数或分析脚本;
  • results/ 保存表格、图形和模型输出;
  • README.md 说明项目入口、运行顺序与数据来源。

目录不必套很多层。只有当一组文件具有明确且长期稳定的共同职责时,才值得增加一层目录。

R 对象使用一致的命名风格

R 与 tidyverse 代码常使用 snake_case:

normalized_counts <- normalize_counts(raw_counts)
sample_metadata <- read.csv("data/raw/sample_metadata.csv")

函数名宜使用动词或动词短语,数据对象宜使用名词:

filter_significant_genes <- function(results, alpha = 0.05) {
  results[results$p_value < alpha, ]
}

significant_genes <- filter_significant_genes(de_results)

逻辑值可以使用能直接读成判断的问题:

is_valid <- TRUE
has_missing_values <- anyNA(sample_metadata)

比起把类型缩写塞进每个名称,更重要的是表达对象在分析中的意义。patient_metadata 通常比 char_df 更有用,因为前者说明内容,后者只说明暂时的存储类型。

避免含糊和冲突

# 含义不清
df1 <- read.csv("data.csv")
result2 <- lm(y ~ x, data = df1)

# 表达角色
patient_data <- read.csv("data/raw/patients.csv")
age_model <- lm(outcome ~ age, data = patient_data)

避免把对象命名为常用函数或特殊缩写,例如 datatablemeanTF。单字符名称适合很短的数学表达或局部循环,不适合贯穿整个分析流程。

R 允许同一个名字被重新赋值,甚至改变类型:

x <- 1:5
x <- "hello"

代码不会因此报错,但一个名字在同一段流程中不断改变含义会增加阅读和调试成本。可以让对象经历清楚的阶段,例如 raw_countsfiltered_countsnormalized_counts;也可以在短而明确的管道中复用同一个语义稳定的名字。

Git 管理代码历史

Git 已经记录每次代码修改,因此受版本控制的脚本应保持稳定文件名:

fit_models.R

修改后提交历史,而不是复制出:

fit_models_v2.R
fit_models_v2_fixed.R
fit_models_final.R

一次提交应表达一个清楚的意图:

feat: add subgroup analysis
fix: correct age-group boundary
docs: explain input data source

Git 特别适合文本代码和配置。大型原始数据、隐私数据、凭据和可以重新生成的临时输出,通常不应直接提交;具体边界应由项目的数据治理要求和 .gitignore 共同决定。

Semantic Versioning 管理正式发布

vMAJOR.MINOR.PATCH 用于对外发布的软件、R 包、命令行工具或稳定接口:

  • MAJOR:引入不兼容变化;
  • MINOR:向后兼容地增加功能;
  • PATCH:向后兼容地修复问题。

例如,一个已发布 R 包可以从 1.2.0 更新到 1.2.1,并在 Git 中建立对应 tag:

git tag v1.2.1

普通分析脚本并不天然需要 Semantic Versioning。若没有使用者依赖它的公开接口,给每个 .R 文件附加 v2.0.0 往往只会重新制造副本混乱。此时提交哈希已经能够准确定位历史状态。

三种版本信息不要混用

要追踪的东西 推荐机制 示例
代码怎样变化 Git commit a1b2c3d
哪次数据或结果快照 ISO 日期或数据版本字段 2025-04-30
发布的软件接口 Semantic Versioning + Git tag v1.2.1

需要复现一次分析时,可以同时记录三者:输入数据的版本或日期、运行代码的 commit,以及关键软件环境。它们回答的是不同问题,不能用一个文件名后缀代替。

最小实践

一个项目不必先制定庞大的规范。先保持以下几条即可:

  1. 文件和 R 对象统一使用有语义的 snake_case;
  2. 原始数据、处理数据、代码和结果分开存放;
  3. 日期只用于真正的时间快照,并统一写成 YYYY-MM-DD
  4. 代码变化交给 Git,不创建 final_final 副本;
  5. 只有发布软件或稳定接口时才使用 Semantic Versioning;
  6. 在 README 中写清项目入口、数据来源和运行方式。

规范的目的不是让每个名字都变长,而是让未来的自己能够判断:这个文件是什么、从哪里来、是否可以重建,以及怎样找到它的历史。