“skills”是我最近一年里维护频率最高的个人项目。单看这个名字确实不起眼,但它几乎解决了我最头疼的一类问题:技能越堆越多、越学越焦虑,最后却说不清楚自己到底会什么、不会什么、下一步该练什么。
我不是把它做成一个花哨的App,也没有引入复杂的任务管理平台,核心就是一套以纯文本为主、配少量脚本的“技能档案系统”。它帮我完成三件事:把模糊的“我想学很多东西”拆成可执行的技能项;给每一类技能记录可验证的证据和熟练度;用轻量脚本做周期性巡检,逼着我去更新而不是遗忘。
如果你跟我一样,觉得自己学了跟没学一样,或者想从一个只会“收藏资料”的状态切换到“真正积累能力”的状态,这篇记录应该能给到一些可复用的思路。我尽量把设计逻辑、文件结构、代码示例和踩坑细节都写清楚,方便你直接照着自己的情况复制一版。
1. 项目起因:为什么我非要建一个“skills”库
1.1 最开始的场景:技能焦虑
大概是从两年前开始,我对“好像什么都会一点,又什么都拿不出手”这种感觉越来越强烈。今天刷到别人说某个框架好用,我就想学框架;明天看到一份岗位要求里写着“熟悉持续集成”,我又觉得这是自己欠缺的部分。于是我的浏览器收藏夹里堆了上百个教程链接,网盘里也存了好几个G的学习资料,但真到了写简历或者接项目的时候,能拿出来讲的东西依然很少。
问题不在资料不够,而在没有一个结构化的东西去承接这些输入。今天看一段教程,明天做一个Demo,后天又换了一个方向,这些东西之间的关联、熟练程度、是否需要复习,全部靠脑子来记。而人脑最不擅长做这种长期跟踪和对比的事情,最后自然就变成了“学完就忘”。
后来我开始尝试用备忘录和Excel去记录学习进度,但都没坚持下来。备忘录太零散,写进去就很难再回头整理;Excel虽然可以做排序和筛选,但每次打开要维护表头、行列、状态,总觉得在做表格而不是在积累技能。核心矛盾是:我需要一个既轻量、又可以长期维护、还能让我随时看清楚全局的机制。
1.2 从待办清单到技能档案
一开始我把它当成To-Do,新建一个清单,写上“学完某某课程”“读完某某书”,完成一项就划掉一项。这样确实带来了一点掌控感,但很快暴露了问题:技能不是一次性的任务,它是需要持续迭代的。
比如我把“熟练掌握Docker”写成了待办,等我能启动一个容器后,我就把它勾掉了。但过了三个月,当我需要在项目里写Compose编排、多阶段构建时,我发现自己的理解远够不上“熟练”。问题就在于,我从来没有定义一个“熟练”的标准,也没有记录我在什么场景下真正用到过这个技能。
所以我把思路做了一个转换:不再把技能当作一个要“完成”的事件,而是当作一份“档案”。每份档案是一个持续更新的页面,记录技能本身、我的水平等级、最近一次练习时间、做过的实际案例以及下一步要补的短板。这样,技能的积累变成了一条有迹可循的曲线,而不是一堆互不关联的勾选记录。
1.3 设计目标与选型原则
在建“skills”之前,我给自己定了几个很明确的原则,避免又掉进“工具选型”的坑里。第一,数据必须是我的,不能只存在某个平台的服务器上;第二,格式必须足够通用,最好能用文本编辑器打开和修改;第三,维护成本要低,不能每次记录都要启动一个很重的应用;第四,要方便统计和巡检,能让我一眼看出哪些技能长期没更新。
按这个思路,我最后选了Markdown加上JSON,再用Git做版本管理。Markdown负责写技能详情,因为它的可读性最好;JSON负责存结构化的元数据,比如技能分类、熟练度、状态、上次更新时间,方便脚本去扫描和统计;Git负责记录整个技能的演变历史。
有人可能会问,为什么不直接用飞书文档、Notion这类工具?它们确实好用,但对个人技能管理来说有几个隐性成本:一是数据结构不透明,导出麻烦;二是它会分散你的注意力,写着写着技能档案就变成了整理文档;三是很难做自动化,比如“自动找出30天没更新的技能”这个需求,在文档工具里实现起来很别扭。我想要的是一套能在我本地随时跑的轻量系统,这也是后面为什么加了一个几十行统计脚本的原因。
2. 核心模块拆解:skills 到底在管什么
2.1 技能的结构化定义
既然叫技能档案,第一个问题就是:一份“技能档案”里应该包含哪些字段?我一开始写得很随意,一个标题加一段感想,结果发现回到主页时根本看不出来自己处于什么阶段。后来我反复迭代,最终稳定到下面这一组字段。
- 技能名称:具体到动词加对象,例如“编写Docker Compose编排文件”,而不是“Docker”。
- 分类:基础能力、专业方向、工具链、软技能,用来做横向对比。
- 熟练度等级:L1了解、L2上手、L3熟练、L4精通。
- 当前状态:学习中、可用、搁置、待复习。
- 最近一次练习时间:用于巡检脚本判断是否过期。
- 证据链接:写过的项目地址、笔记链接、面试中讲过的案例,用来说明“我确实做过”。
- 下一步动作:一句话说清楚下一次尝试要提升什么。
核心逻辑其实很简单:技能没有客观分数,你给自己定义清楚“下一步动作”之后,能力提升才有抓手。举个例子,我写“Python脚本自动化”,熟练度标成L3“熟练”,但如果没有证据,这个L3就是编的。后来我给自己加了一条规则:没有证据链接的熟练度最多只能标到L2,想上L3必须有一个能拿出来展示的东西。
2.2 技能分类与技能树
技能多了以后,分类就成了刚需。我给自己的所有技能分了四个大类,每个大类下再拆出更细的小类,形成一棵技能树。
- 基础能力:数据结构、算法、操作系统、网络基础、英语阅读。
- 专业方向:前端开发、后端开发、数据库设计、系统设计、数据分析。
- 工具链:Git、Docker、CI/CD、自动化测试、编辑器效率。
- 软技能:文档写作、沟通协作、复盘方法、项目推进。
这里要特别说明一点:分类不是一成不变的,它会随着你对技能理解的加深而调整。我最初把“文档写作”归到了工具链里,但后来发现它其实应该属于软技能,因为它的学习方式和使用场景跟工具链差别很大。定期把技能树拿出来重构一次,本身就是一次很好的能力盘点。
针对每一种分类,我还会做一个“技能水平矩阵”,用二维表格把各个技能和熟练度等级对应起来。这样看全局的时候特别直观:哪个大类长期空白、哪个技能很久没更新、哪块能力最需要补,一目了然。
2.3 学习闭环:输入、实践、复盘
建了分类和字段之后,我发现记录系统本身还不够,因为如果没有一套学习节奏,档案最终会变成一个“摆设”。于是我把技能的积累过程拆成了三个环节:输入、实践、复盘。
输入指的是看文档、读源码、上课、拆项目,它的产出是理解和笔记。实践是指把输入用起来,写一个小项目、修一个Bug、做一次分享,都是实践。复盘是指回到技能档案里更新熟练度、补充证据、写下失败点和下一步动作。
我给自己定的节奏是:输入之后必须有一到两次完整的实践,否则下次复盘时不会更新这个技能的熟练度;每个技能在最开始就写清楚“什么样的实践算完成”,比如“用Docker部署一个带数据库的服务”,这就比“多练习”要具体得多。这样整个系统才能形成一个闭环,而不是只有记录和遗忘。
3. 实操记录:从零搭一个可用的 skills 系统
3.1 目录结构与初始化
如果要从头复制我的方案,你只需要一个支持Markdown的文本编辑器、Git、以及随意一个能跑Python或者Shell脚本的环境。整个仓库的结构如下。
skills/ ├── README.md ├── skills.json ├── templates/ │ └── skill-template.md ├── entries/ │ ├── docker-compose.md │ ├── python-automation.md │ ├── sql-query-optimization.md │ └── ... ├── scripts/ │ ├── update-metadata.py │ └── review-reminder.py └── archived/ └── old-idea-that-i-dropped.mdREADME.md是整棵技能树的展示页,我会在右上角放一段自动生成的统计信息,比如技能总数、各等级分布、30天未更新数量。skills.json存每个技能的元数据,entries里是每份技能档案的完整Markdown内容。scripts里放两个脚本,一个负责解析Markdown内容并更新JSON,另一个负责生成复习提醒。archived用来归档那些我明确放弃的技能,避免删除后丢失经验。
初始化的时候我先创建了模板,然后把当时能够想到的核心技能分成四个大类,一篇一篇地补进去。这个过程别贪多,我一开始写了二十多个,结果很多细节根本填不上,最后主动砍到了十来个。技能档案的质量远比数量重要。
3.2 技能模板怎么写
我建议每个技能档案的开头都放一个YAML或者JSON格式的元信息块,这样脚本解析起来非常方便。下面是我在用的模板。
--- name: 编写Docker Compose编排文件 category: 工具链 level: L3 status: 可用 last_practice: 2025-02-18 evidence: - https://github.com/yourname/deploy-example next_action: 用Compose完成一个带持久化卷的多服务部署 --- ## 当前掌握 能编写多容器编排配置,理解网络、卷、环境变量等基本概念。 ## 实践记录 - 2025-02-18:完成项目A的部署配置,包含nginx、api、postgres三个服务。 - 2024-12-30:复习Compose中depends_on的启动顺序问题。 ## 待提升 - 多阶段构建与Compose配合时的镜像大小控制。 - 如何优雅地处理配置更新而不中断服务。 ## 教训 不要把Compose文件只当作“启动命令”,它本质上是基础设施配置的一部分,要纳入版本管理,不然环境漂移会带来很多问题。这个模板最有用的部分是“待提升”和“教训”。如果只有“掌握了什么”,很容易写完就再也不看;“待提升”是给自己留的钩子,下一次实践可以直接从这里找方向;“教训”则是把踩过的坑沉淀下来,避免同样的问题反复出现。
3.3 用脚本做状态统计
光有Markdown还不够,我需要一个方式快速知道整个技能库的健康状态。于是写了一个非常简单的Python脚本,核心逻辑是用正则去解析Markdown开头的元信息,然后汇总成统计结果。
import json import re from pathlib import Path from datetime import datetime, date SKILLS_JSON = Path("skills.json") ENTRIES_DIR = Path("entries") skills = [] for md in ENTRIES_DIR.glob("*.md"): text = md.read_text(encoding="utf-8") meta_match = re.search(r"^---\n(.*?)\n---", text, re.S | re.M) if not meta_match: continue meta = dict( re.findall(r"^(\w+):\s*(.*)$", meta_match.group(1), re.M) ) skills.append({ "name": meta.get("name", md.stem), "category": meta.get("category", "未分类"), "level": meta.get("level", "L1"), "status": meta.get("status", "未知"), "last_practice": meta.get("last_practice", "1970-01-01"), }) skills.sort(key=lambda x: x["last_practice"], reverse=True) with SKILLS_JSON.open("w", encoding="utf-8") as f: json.dump(skills, f, ensure_ascii=False, indent=2) today = date.today() stale = [] for s in skills: last_date = datetime.strptime(s["last_practice"], "%Y-%m-%d").date() if (today - last_date).days > 30: stale.append(s) print(f"技能总数: {len(skills)}") print(f"30天未更新: {len(stale)}") for s in stale: print(f" - {s['name']} (上次: {s['last_practice']})")这个脚本解决了我最大的痛点:让我不用打开每个文件就能知道哪些技能正在“腐烂”。很多人以为技能是学过了就一直有效,但实践后才发现,长期不用的熟练度会退得很快。定期看到“30天未更新”的列表,比任何打卡系统都管用。
3.4 复习提醒与巡检
光统计还不够,我后来加了第二个脚本,专门用来生成复习列表。它的逻辑很简单:按“间隔重复”的思想,如果一个技能最近一次实践时间是7天内,它就不需要打扰我;7到30天之间的,属于“该看一眼”;超过30天的,属于“重点复习”。
我是用Shell配合cron跑这个脚本的,每周五下午会收到一条提醒。平时不主动看客户端,只在固定时间做巡检,这样做的好处是既能保持系统在运转,又不会让“维护技能库”本身变成一种逃避学习的仪式感。
#!/bin/bash cd ~/skills python3 scripts/update-metadata.py python3 scripts/review-reminder.py复习提醒的输出会根据状态给出不同建议,比如“该技能超过30天没有实践,建议本周安排一个30分钟的练习场景”。这里的关键不是让你去“看资料”,而是安排一个具体的练习场景。因为没有场景的复习,本质上只是在骗自己“我看过了”。
3.5 与现有工具打通
让这套系统真正融入日常工作,还需要和本来就高频使用的工具打通。我把skills仓库作为本地Git仓库,同时定期推到私有仓库做备份。每次提交的时候,Commit message直接写当天做了哪个技能的实践,比如“perf: 练习Docker Compose卷挂载细节”。这样Git历史本身就变成了一份时间线,回头复盘的时候能非常清楚地看到自己把时间花在了哪里。
代码编辑器方面,我用VS Code编辑Markdown,通过Todo Tree插件把“待提升”里的条目高亮出来。平时写代码时如果想到某个技能点,会随手打开对应的档案追加一行备注。我不追求任何时候都立刻更新,但会保证当天做过的实践最晚在当天结束前同步进去。
如果你习惯用Obsidian,也可以直接把这个仓库放到Obsidian的Vault里,配合双链功能,把技能档案和笔记系统打通。我试过这种方式,它的好处是可以在技能档案里引用具体的学习笔记,坏处是Obsidian的数据库文件有时候会带来额外的同步负担。个人体感是,纯Markdown加Git仓库再加一个简单的文件浏览,已经足够干净和高效。
4. 常见问题与避坑指南
4.1 技能太多,不知道怎么分类
我在刚开始整理时最常犯的错是分类过细,比如前端里再分“框架”“状态管理”“样式方案”“构建工具”,最后每个小类下面只有一两条记录,维护成本反而变大。后来我调整策略:大类保持四到六个,小类只在必要时出现,同一个技能如果横跨多个类,就选它当前最需要提升的那个类,而不是试图定义得面面俱到。
如果遇到一个技能你明显想分成好几个方向,那大概率说明它已经不止是一个“技能”,而是一个“技能族”。比如“数据分析”这个词太大了,它下面可能包含SQL、Python、可视化、统计学基础,应该拆成多个独立档案。拆分的标准是:每个档案都必须有独一无二的“证据链接”和“下一步动作”,如果两个技能共享同一份证据,那它们就应该合并。
4.2 记了模板但坚持不下去
很多人在尝试这种系统时会遇到同一个情况:头三天记录很勤快,后面就荒废了。我的体会是,坚持不下去不是因为懒,而是因为系统里塞了太多“伪技能”。所谓伪技能,就是你其实根本没有明确的练习场景和学习计划,只觉得自己“应该学”。
解决办法是缩减库存。我把所有技能档案翻出来,强制自己给每一项写“最近一次实践”和“证据链接”,写不出来的全部归档。经过这轮清理,真正有资格待在主列表里的技能少了一大半,但每一项都是我近期实际在用的东西。宁可维护五份有证据的技术档案,也不要维护五十份只写了一行标题的空壳。
另外,我给自己立了一个规矩:每次新增技能前,必须先写清楚为什么现在需要它,以及准备拿它做什么。答不出来就不允许开新档案。这一条帮我把很多冲动学习挡在门外,特别有效。
4.3 实战验证比记录更重要
这个系统能记录“我知道什么”,但真正决定能力的是“我做过什么”。我见过很多人把技能档案整理得非常漂亮,熟练度也标得很高,但遇到真问题依然出不了手。所以我给自己设了一个很硬性的规则:熟练度从L2升到L3,必须有一个实际项目或者一份有效证据,而不是自己觉得“差不多会了”。
实践场景怎么找?不一定非要重新造一个大项目。我可以从日常工作中找,比如给团队写一个脚本、修复一个长期存在的Bug、把手工操作流程变成自动化脚本,这些都可以当成证据。我还可以从已有代码里去挖,把一个自己写过的小模块重构一遍,然后记录重构前后的差异,这样证据就有了。关键是不要想着“等我准备好了再实践”,而是把当前手头一个最小的问题当成练习场。
4.4 常见问题速查表
| 问题 | 原因 | 我的解决方案 |
|---|---|---|
| 技能档案写了一堆,但不想回看 | 缺少定期巡检机制 | 每周固定时间跑复习提醒脚本,强制浏览 |
| 熟练度越标越高,心里发虚 | 没有硬性证据门槛 | 没有证据不升L3,升级前必须补实践记录 |
| 分类太细,维护成本高 | 把技能族和技能混为一谈 | 按“是否有独立证据”拆分或合并 |
| 学了一段时间感觉没进步 | 只有输入,没有实践 | 每项技能必须定义清楚的实践场景 |
| 想新增技能,怕三分钟热度 | 缺少加入门槛 | 必须写“为什么现在需要”和“下一步动作” |
我在实际维护过程中发现,这张表和档案系统一样,也需要定期更新。因为随着你的能力边界不断变化,旧规则会变得不再合适。比如一开始“能否写出完整Compose文件”可能就算合格,但后面你一定会遇到更复杂的部署场景,这时候就要把标准往上提。
写在最后
如果你也准备建一个自己的“skills”仓库,我比较推荐从最小版本开始:一个README、一个template、三个真实技能的档案,一个能统计更新状态的脚本。不要一上来就设计几十个字段,也不要想着把分类一次做完美。先跑一个月,感受一下自己在什么时候愿意更新、什么时候不想碰它,再根据真实反馈去调整。
我个人最受益的一个习惯,是每周五下午花十分钟跑一遍巡检脚本,看到那些超过30天没更新的技能后,挑一个最容易的给它安排一个半小时的实践。哪怕只是把一条旧命令重新跑一遍,也比打一堆“今天学习了”的卡有价值。这个系统真正改变我的不是记录的方式,而是让我开始把“知道一个概念”和“拥有一个技能”分得清清楚楚。