1. 当“skills”成为一个项目标题:我看到的不是技能清单,而是一套能力封装系统
第一次看到“skills”这个词被当作项目标题,我脑子里冒出来的不是“技能列表”这种平平无奇的理解。在工程实践里,一个以“skills”命名的项目,通常意味着它要解决的是一个非常具体的问题:如何把散落在各处的、隐性的、口口相传的操作能力,变成可复用、可组合、可交付的结构化模块。这件事听起来简单,做起来极其磨人。
我接触过不少团队,他们一开始都觉得“技能”这东西太虚了,不就是写个文档、录个视频、搞个内部培训吗?但真正做过一轮之后就会发现,文档写了没人看,视频录了没人点,培训讲完就忘。问题出在哪儿?出在“技能”没有被当作一种工程对象来对待。它没有接口、没有版本、没有依赖声明、没有测试用例,甚至连“调用方式”都不统一。你让一个人去复用另一个人的技能,就像让一个只会 Python 的人去调一个用汇编写的函数——理论上可行,实际上痛苦。
所以这篇内容,我想围绕“skills”这个项目标题,把我在实际工作中踩过的坑、总结出来的方法、以及一套可落地的能力封装思路,完整地摊开来讲。不管你是做内部工具链、做团队知识管理、还是做面向开发者的平台产品,只要你的项目里出现了“skills”这个词,下面这些内容大概率能帮你少走两三个月的弯路。
提示:本文不讨论任何具体平台或框架的安装配置,只讲“能力封装”这件事本身的逻辑、结构和实操方法。所有案例均为虚构代称,不涉及任何真实机构或项目。
2. 为什么“技能”一旦要复用,就必然走向结构化封装
2.1 从“我会”到“你能用”:技能传递的断层在哪里
我们先来拆一个最基础的场景。假设团队里有个 A 同学,他特别擅长处理某类数据清洗任务,速度快、准确率高。现在 B 同学也遇到了类似任务,他想复用 A 同学的能力。最直接的做法是什么?让 A 同学口述一遍,或者写个文档。但实际结果往往是:A 同学讲的时候 B 同学觉得懂了,自己上手就卡住;文档写了几页,关键参数没写清楚,边界条件没提,异常处理没讲。
这个断层的本质是什么?是隐性知识没有被显性化,显性知识没有被结构化,结构化知识没有被接口化。A 同学脑子里的“技能”包含了大量默认假设:数据格式默认是什么、缺失值默认怎么处理、性能瓶颈默认在哪里、什么情况下该放弃当前策略。这些东西他不说,不是故意藏私,而是他自己都没意识到这些是“需要说”的。
所以一个以“skills”为核心的项目,第一件要做的事不是写代码,而是定义技能的边界和契约。你得先回答:这个技能接受什么输入、产出什么输出、在什么条件下会失败、失败后应该怎么恢复。这四个问题答不清楚,后面所有封装都是白搭。
2.2 技能封装的四个层次:从脚本到服务
我在实际项目中把技能封装分成四个层次,每个层次的复用成本和适用范围完全不同。你可以对照自己的项目看看现在处在哪一层。
| 层次 | 形态 | 复用方式 | 适用场景 | 主要痛点 |
|---|---|---|---|---|
| L1 | 一次性脚本 | 复制粘贴 | 个人临时任务 | 参数硬编码,换环境就废 |
| L2 | 参数化函数/模块 | 导入调用 | 同语言同项目 | 依赖不透明,版本易冲突 |
| L3 | 独立可执行单元 | 命令行/API调用 | 跨项目跨语言 | 接口不稳定,文档滞后 |
| L4 | 注册式技能服务 | 发现+编排+调用 | 平台化多技能协同 | 治理复杂,调试链路长 |
大多数团队卡在 L2 到 L3 之间。函数写了不少,但每个函数都假设自己运行在特定的上下文里:特定的目录结构、特定的环境变量、特定的依赖版本。一旦要跨项目复用,就得把整个上下文一起搬过去,搬运成本高到让人放弃。
L3 的关键突破是把上下文变成显式契约。一个技能单元应该自带它的运行说明:需要什么运行时、需要什么依赖、需要什么输入格式、产出什么输出格式。这些东西不是写在 README 里就完了,而是要能被机器读取和校验。我见过太多项目,README 写得漂漂亮亮,实际跑起来缺三个环境变量、两个系统库、一个配置文件,全都没提。
L4 则是在 L3 的基础上增加了发现机制和编排能力。当你有几十上百个技能单元时,光靠人记是记不住的。你需要一个注册中心,让每个技能声明自己的能力标签、输入输出模式、性能特征,然后让调用方能够按需发现和组合。这一步的复杂度会陡增,因为你要处理版本兼容、依赖冲突、调用链追踪、失败重试等一系列分布式系统才有的问题。
2.3 一个反直觉的结论:技能越通用,越难复用
很多人觉得,一个技能如果设计得足够通用,应该能被更多人复用。但实际经验恰恰相反:过度通用的技能,往往没人用。原因很简单,通用意味着抽象层次高,抽象层次高意味着调用方需要做更多的“适配工作”才能把具体问题映射到通用接口上。这个适配成本,经常比直接写个专用脚本还高。
我自己的做法是:先做专用,再做通用。先针对一个具体场景做一个能跑通的技能单元,把它打磨到稳定可用。然后当第二个、第三个类似场景出现时,再从中提取共性,抽象出通用接口。这时候的通用是有真实用例支撑的,不是拍脑袋想出来的。而且抽象出来的接口,因为经过了多个具体场景的验证,边界条件会清晰得多。
注意:不要一开始就追求“大而全的技能框架”。我见过一个项目,花了三个月设计了一套“万能技能协议”,结果第一个实际技能接入就发现协议里缺了三个关键字段,改协议又导致前面写的适配层全部重来。先跑通一个,再抽象,这个顺序不能反。
3. 技能描述文件的设计:让机器和人都能读懂同一个技能
3.1 为什么需要一个独立的描述文件
技能封装到 L3 之后,最核心的产物不是代码本身,而是一个技能描述文件。这个文件的作用是:让调用方在不阅读源码的情况下,就能知道这个技能能做什么、怎么调用、会返回什么、可能出什么错。它既是给人看的文档,也是给机器解析的契约。
我试过把描述信息直接写在代码注释里,然后用工具提取。实践下来问题很多:注释格式不统一、关键信息缺失、提取工具维护成本高。后来改成独立的描述文件,用结构化格式(YAML 或 JSON)来写,情况就好多了。独立文件的好处是:格式强制统一、可以被 CI 流水线校验、可以被注册中心直接读取、可以独立于代码版本进行管理。
一个最小可用的技能描述文件,至少应该包含以下字段:
skill_id: data_clean_basic version: 1.2.0 display_name: 基础数据清洗 description: 对结构化表格数据进行缺失值填充、异常值剔除和类型标准化 inputs: - name: source_path type: string required: true description: 输入数据文件路径,支持 csv 和 parquet - name: fill_strategy type: enum values: [mean, median, mode, drop] default: mean outputs: - name: cleaned_path type: string description: 清洗后数据文件路径 - name: report type: object description: 清洗报告,包含处理行数、剔除行数、填充统计 runtime: language: python version: ">=3.9" dependencies: - pandas>=1.5 - numpy>=1.23 errors: - code: INPUT_NOT_FOUND description: 输入文件不存在或不可读 - code: SCHEMA_MISMATCH description: 输入数据列类型与预期不符这个文件看起来简单,但每一个字段都有讲究。skill_id要全局唯一且稳定,不能随版本变化;version要遵循语义化版本规范,因为调用方需要根据版本判断兼容性;inputs里每个参数的type和required要明确,否则调用方没法做参数校验;errors里列出的错误码要覆盖所有可预期的失败场景,不能只写“未知错误”。
3.2 输入输出模式:技能之间协作的基石
技能描述文件里最重要的部分,是输入输出的模式定义。这决定了技能能不能被自动编排。如果两个技能要串联,前一个的输出必须能匹配后一个的输入。这个匹配不是靠人眼看的,而是靠模式比对。
我在设计输入输出模式时,遵循几个原则。第一,类型要具体。不要写object就完了,要写清楚这个 object 有哪些字段、每个字段什么类型、哪些必填。第二,格式要明确。同样是字符串,是文件路径、URL、JSON 字符串还是普通文本,差别很大。第三,约束要可校验。比如数值范围、字符串长度、枚举取值,这些约束要能被自动检查。
outputs: - name: report type: object schema: total_rows: type: integer description: 原始数据总行数 dropped_rows: type: integer description: 被剔除的行数 filled_columns: type: array items: type: string description: 被填充的列名列表 fill_values: type: object description: 各列使用的填充值,键为列名,值为填充值有了这样的模式定义,编排引擎就可以做自动类型检查:如果前一个技能输出的report里没有total_rows字段,而下一个技能需要这个字段作为输入,引擎就能在运行前报错,而不是等到运行时才崩。
3.3 版本管理与兼容性声明
技能描述文件的版本管理,比代码版本管理更容易被忽视。我踩过的一个坑是:技能代码升级了,行为变了,但描述文件没更新,调用方按旧描述调用,结果拿到意料之外的结果。
后来我强制要求:任何影响输入输出行为的变更,必须同时更新描述文件的版本号,并在变更日志里说明兼容性。具体规则是:
- 补丁版本(如 1.2.0 → 1.2.1):只修 bug,输入输出模式不变,调用方无需改动。
- 次版本(如 1.2.0 → 1.3.0):新增可选输入或新增输出字段,旧调用方式仍然可用。
- 主版本(如 1.2.0 → 2.0.0):删除或重命名输入输出字段、改变必填性、改变错误码含义,旧调用方式会失败。
这个规则看起来是常识,但实际执行时,很多人会觉得“就改一个小字段而已,不用升主版本吧”。结果就是调用方在某个深夜被线上告警叫醒,发现技能返回的数据结构变了。所以我的建议是:宁可版本号升得激进一点,也不要让调用方猜。
4. 技能注册与发现:当技能数量超过二十个之后
4.1 从文件目录到注册中心:一个必然的演进
技能少的时候,用文件目录管理就够了。每个技能一个文件夹,里面放描述文件和代码,调用方按路径引用。十个以内的技能,这种方式简单直接,没什么问题。
但技能数量一旦超过二十个,文件目录就开始失控了。调用方记不住哪个技能在哪个路径,不知道某个技能有没有现成的,甚至不知道某个技能存不存在。更麻烦的是,当技能有多个版本时,目录结构会变得极其混乱:是每个版本一个目录,还是用软链接切换?是保留所有历史版本,还是只留最新的?
这时候就需要一个注册中心。注册中心的核心功能很简单:技能提供方把描述文件注册进去,调用方按条件查询。但要做好,需要考虑几个关键设计。
第一,注册的粒度。是按技能注册,还是按技能版本注册?我的做法是按版本注册,每个版本一条记录,但查询时默认返回最新稳定版。这样既保留了历史版本的追溯能力,又不会让调用方被版本选择困扰。
第二,发现的方式。最基础的是按skill_id精确查找。但实际使用中,调用方经常不知道确切的skill_id,只知道“我要找一个能清洗数据的技能”。所以注册中心需要支持按标签、按输入输出类型、按描述关键词进行模糊搜索。
第三,健康检查。注册中心里的技能,不一定都是可用的。可能依赖的服务挂了,可能运行时环境出了问题。注册中心需要定期对已注册技能做健康检查,把不可用的技能标记出来,避免调用方踩坑。
4.2 标签体系:让技能能被“找到”的关键
标签体系是注册中心里最容易被低估的部分。我见过很多项目,标签就是随便打几个关键词,结果搜索时要么搜不到,要么搜出一堆不相关的。好的标签体系应该从三个维度来设计:
- 能力维度:这个技能做什么?比如
>pipeline_id: daily_data_process version: 1.0.0 description: 每日数据清洗、统计与报告生成流水线 steps: - step_id: clean skill_ref: data_clean_basic@1.2.0 inputs: source_path: "{{ pipeline_input.raw_path }}" fill_strategy: median outputs: cleaned_path: "{{ steps.clean.outputs.cleaned_path }}" - step_id: stats skill_ref: data_stats_summary@2.0.1 inputs: data_path: "{{ steps.clean.outputs.cleaned_path }}" outputs: stats_report: "{{ steps.stats.outputs.report }}" - step_id: report skill_ref: report_generate_pdf@1.0.0 inputs: stats: "{{ steps.stats.outputs.stats_report }}" output_path: "/tmp/daily_report.pdf"这个描述文件里,
{{ }}是变量引用语法,用来在步骤之间传递数据。pipeline_input是流水线的外部输入,steps.xxx.outputs.yyy是某个步骤的输出。这种声明式的写法,让流水线的数据流一目了然,也方便编排引擎做静态检查:如果某个步骤引用了不存在的输出字段,引擎可以在执行前就报错。5.3 编排中的错误传播与补偿
编排最棘手的问题不是正常流程,而是异常流程。一个五步的流水线,第三步失败了,前两步已经执行完,第四步和第五步还没开始。这时候应该怎么办?
我的做法是分三种情况处理:
- 可重试错误:比如网络抖动、临时资源不足。这种错误自动重试,重试次数和间隔在编排描述文件里配置。
- 可跳过错误:比如某个可选步骤失败,但主流程可以继续。这种错误记录日志后跳过,继续执行后续步骤。
- 不可恢复错误:比如输入数据格式完全错误、依赖服务永久不可用。这种错误立即终止流水线,并触发补偿逻辑。
补偿逻辑是编排里最容易被忽视的部分。所谓补偿,就是“回滚”已经执行完的步骤。但技能执行往往不是事务性的,回滚没那么容易。比如一个技能写了文件,回滚就是删文件;一个技能发了消息,回滚就是发一条更正消息。这些补偿操作需要在编排描述文件里显式定义,不能指望自动推导。
steps: - step_id: clean skill_ref: data_clean_basic@1.2.0 on_failure: retry max_retries: 3 retry_interval: 10s compensate: skill_ref: file_delete@1.0.0 inputs: path: "{{ steps.clean.outputs.cleaned_path }}"这个补偿定义的意思是:如果
clean步骤最终失败,就调用file_delete技能把可能已经产生的中间文件删掉。这样即使流水线中断,也不会留下垃圾数据。6. 技能质量保障:怎么知道一个技能是真的“能用”
6.1 技能测试与普通代码测试的区别
技能测试和普通代码测试最大的区别在于:技能测试要验证的是契约,而不仅仅是逻辑。普通代码测试关注“给定输入,函数返回什么”,技能测试还要关注“给定输入,技能是否按照描述文件声明的方式返回”。
这意味着技能测试需要覆盖几类特殊场景:
- 输入模式校验:传入不符合描述文件声明的输入,技能是否返回预期的错误码?
- 输出模式校验:技能返回的输出,是否符合描述文件里声明的结构?
- 边界条件:空输入、超大输入、特殊字符输入,技能行为是否符合预期?
- 依赖缺失:当某个依赖不可用时,技能是否给出清晰的错误信息?
我通常会给每个技能写一套“契约测试”,用描述文件自动生成测试用例。比如描述文件里声明
source_path是必填的,契约测试就自动生成一个不传source_path的用例,验证技能是否返回INPUT_NOT_FOUND错误。这样描述文件和实际行为就不会脱节。6.2 技能成熟度分级:从实验到稳定
不是所有技能都值得被同等对待。有些技能是刚写出来的,只在一个场景下跑通过;有些技能已经经过多个项目验证,稳定可靠。如果注册中心里所有技能都混在一起,调用方就没法判断哪个能放心用。
所以我建议给技能打上成熟度标签,至少分三级:
- 实验级:刚开发完成,只在有限场景验证过。调用方使用需自行承担风险,注册中心会明确标注。
- 可用级:在至少两个不同场景下验证通过,有基本的错误处理和文档。可以用于非关键路径。
- 稳定级:经过多个项目长期使用,有完整的测试覆盖和监控,有明确的维护责任人。可以用于关键路径。
成熟度的升级不是自动的,需要技能提供方主动申请,并附上验证证据。注册中心可以设置一个简单的评审流程,由其他使用过该技能的人确认。这样既能保证成熟度标签的可信度,又不会让评审成为瓶颈。
6.3 技能使用反馈的收集与利用
技能好不好用,最终要看调用方的反馈。但调用方通常不会主动写反馈,除非遇到了严重问题。所以反馈收集要设计得足够“无感”。
我的做法是在技能调用链路里埋几个自动采集点:
- 调用成功率:技能被调用的总次数和失败次数,按错误码分类统计。
- 执行耗时分布:技能执行的 P50、P95、P99 耗时,用来判断性能是否退化。
- 输入输出异常率:输入不符合模式、输出不符合模式的比率,用来发现契约漂移。
- 调用方评分:在调用结束后,给调用方一个可选的评分入口,一到五星,外加一个自由文本反馈框。
这些数据汇总到技能注册中心,形成每个技能的质量画像。当某个技能的成功率突然下降,或者耗时突然上升,注册中心可以自动发出告警,提醒技能提供方排查。这样就把被动的“等用户报bug”变成了主动的“发现质量退化”。
7. 我在技能封装项目里踩过的几个典型坑
7.1 坑一:把技能当函数写,忽略了运行环境
早期我做技能封装时,习惯性地把技能当成一个函数来写:输入参数、处理逻辑、返回结果。代码本身没问题,但一放到不同环境里就各种报错。后来才意识到,技能和函数的本质区别在于:函数运行在确定的上下文里,技能运行在不确定的环境里。
函数知道自己运行在哪个进程、哪个目录、有哪些环境变量。技能不知道。技能可能被命令行调用、被 API 调用、被编排引擎调用,每次调用的环境都可能不同。所以技能代码里不能有任何对环境隐式假设。路径要用绝对路径或从输入参数获取,环境变量要在描述文件里声明,临时文件要写到指定的工作目录。
这个坑我踩了不止一次。最典型的一次是,技能代码里用了
os.getcwd()来定位一个配置文件,本地测试没问题,部署到服务器上工作目录变了,直接找不到文件。后来改成所有路径都从输入参数传入,问题才彻底解决。7.2 坑二:版本升级没有考虑调用方兼容性
有一次我们升级了一个数据清洗技能,把默认的缺失值填充策略从
mean改成了median。改完之后,所有依赖这个技能的流水线输出都变了,下游的统计报告数字全部偏移。调用方找过来的时候,我们才意识到问题的严重性。这个坑的本质是:技能的行为变更,对调用方来说就是接口变更。即使输入输出模式没变,行为变了也是不兼容的。后来我们定了一条规矩:任何影响输出结果的变更,必须升主版本号,并且在变更日志里明确写出“行为变更”。调用方在引用技能时,必须指定完整版本号,不能用“最新版”这种模糊引用。
7.3 坑三:技能描述文件和实际行为不一致
描述文件里写的是“支持 csv 和 parquet”,实际代码只处理了 csv,parquet 分支根本没实现。这种不一致在早期很常见,因为描述文件是手写的,代码是另一个人写的,两边没有强制同步机制。
后来我们加了一个 CI 检查:每次代码提交,自动解析描述文件,生成一组契约测试用例,跑一遍。如果实际行为与描述不符,CI 直接失败,不允许合并。这个检查加上之后,描述文件和实际行为的不一致率大幅下降。
7.4 坑四:编排流水线没有超时控制
一条流水线跑了六个小时还没结束,排查发现是某个技能卡在了一个死循环里。因为没有超时控制,整个流水线就一直挂着,占着资源不释放。
后来我们在编排引擎里加了强制超时:每个步骤有独立的超时时间,整条流水线也有总超时时间。超时后强制终止,并触发补偿逻辑。超时时间不是拍脑袋定的,而是根据历史执行数据来设置:取 P99 耗时的 1.5 倍作为默认超时,特殊步骤可以单独配置。
8. 技能项目的长期维护:怎么让技能库不变成“技能坟场”
8.1 技能废弃流程:优雅地下线一个技能
技能库用久了,总会有一些技能没人用了,或者被更好的技能替代了。这些技能如果一直留在注册中心里,会干扰调用方的选择。但直接删掉又不行,因为可能还有调用方在引用。
我的做法是设计一个废弃流程:
- 标记废弃:技能提供方在注册中心把技能标记为“已废弃”,并注明替代技能。
- 通知调用方:注册中心自动通知所有最近调用过该技能的调用方,告知废弃计划和替代方案。
- 保留过渡期:废弃标记后,技能仍然可用,但调用时会收到警告。过渡期至少三个月。
- 归档:过渡期结束后,技能从活跃列表中移除,但描述文件和代码归档保存,以备追溯。
这个流程的关键是“通知”和“过渡期”。没有通知,调用方不知道技能要废弃;没有过渡期,调用方来不及迁移。两者缺一不可。
8.2 技能库的定期巡检
技能库和代码库一样,需要定期巡检。我通常每季度做一次,检查以下几项:
- 僵尸技能:超过六个月没有任何调用的技能,确认是否还需要保留。
- 版本碎片:同一个技能存在多个版本,且旧版本仍有调用。推动调用方升级到最新稳定版。
- 依赖过期:技能声明的依赖版本是否已经过时,是否有安全更新需要跟进。
- 文档滞后:描述文件的最后更新时间和代码的最后提交时间是否差距过大。
- 质量退化:成功率、耗时等指标是否相比上季度有明显下降。
巡检结果形成一份报告,发给所有技能提供方和主要调用方。该废弃的废弃,该升级的升级,该修文档的修文档。这样技能库才能保持活力,不会慢慢变成没人敢用的“坟场”。
8.3 技能贡献的激励机制
技能库要持续增长,需要有人愿意贡献技能。但贡献技能是一件费力的事:要写代码、写描述文件、写测试、做验证。如果没有激励,很少有人愿意主动做。
我见过比较有效的激励方式有几种。一种是内部积分:贡献一个稳定级技能积多少分,积分可以兑换一些实际资源。一种是署名权:技能描述文件里保留贡献者信息,调用方能看到是谁贡献的。还有一种是评审权:贡献过稳定级技能的人,可以获得技能成熟度评审的投票权。
这些激励方式不一定都适用,但核心思路是一致的:让贡献技能的人获得可见的回报。回报不一定是物质的,但必须是真实的、可感知的。
9. 一个虚构的完整案例:从零搭建一个技能库的三十天
为了把上面的内容串起来,我虚构一个案例。假设有一个五人小团队,要在一个月内搭建一个内部技能库,支撑日常的数据处理和分析工作。以下是他们的大致节奏,我按天拆解关键动作。
第一周:定义契约和跑通第一个技能。第一天到第二天,团队一起讨论技能描述文件的字段设计,确定最小可用字段集。第三天,选一个最常用的数据清洗场景,写第一个技能。第四天,为这个技能写描述文件和契约测试。第五天,搭建一个最简单的注册中心,用文件目录实现,能按
skill_id查找即可。第二周:扩展到五个技能,引入版本管理。第六天到第八天,再写四个技能,覆盖统计、可视化、格式转换、报告生成。第九天,把注册中心从文件目录升级为轻量数据库,支持按标签搜索。第十天,为所有技能打上版本号,制定版本升级规则。
第三周:实现串行编排,跑通第一条流水线。第十一天到第十三天,设计编排描述文件格式,实现一个最简单的串行编排引擎。第十四天,用五个技能串一条完整流水线,从原始数据到最终报告。第十五天,给编排引擎加超时控制和错误重试。
第四周:质量保障和文档完善。第十六天到第十八天,为所有技能补齐契约测试,接入 CI。第十九天,给技能打成熟度标签,区分实验级和可用级。第二十天,写调用方使用指南,包括如何发现技能、如何调用、如何处理错误。
这个节奏看起来紧凑,但实际执行时大概率会延期。延期不可怕,可怕的是为了赶进度跳过契约设计和测试。我见过太多项目,前两周跑得飞快,第三周开始还技术债,第四周直接失控。宁可第一周慢一点,把契约定清楚,后面才能快起来。
10. 关于技能封装,我最后想分享的几个实操心得
第一个心得:描述文件先行。写技能代码之前,先把描述文件写出来。描述文件写不清楚,说明你对这个技能的边界还没想清楚。描述文件写清楚了,代码只是填空。
第二个心得:错误码要具体。不要用
UNKNOWN_ERROR这种万能错误码。每个可预期的失败场景都应该有独立的错误码。调用方根据错误码决定重试、跳过还是终止,错误码越具体,调用方的处理逻辑越精确。第三个心得:技能粒度宁小勿大。一个技能只做一件事。做数据清洗的技能不要顺便做数据统计,做统计的技能不要顺便生成报告。粒度小,复用性才高,编排才灵活。把多个小技能串起来,比写一个大而全的技能要好维护得多。
第四个心得:调用日志要包含技能版本。每次技能调用,日志里必须记录调用的技能 ID 和完整版本号。这样出问题时,可以快速定位是哪个版本的技能行为异常。没有版本信息的日志,排查起来就像大海捞针。
第五个心得:定期清理,不要囤积。技能库不是越大越好。没人用的技能、被替代的技能、质量不达标的技能,该废弃就废弃。一个干净、精选的技能库,比一个庞大、混乱的技能库有价值得多。
这些心得没有什么高深的理论,都是实际做项目时一点一点积累下来的。技能封装这件事,技术难度不算高,但细节极其繁琐。能把细节处理好,技能库就能真正成为团队的资产;处理不好,就会变成没人维护的负担。希望上面这些内容,能帮你少踩几个我已经踩过的坑。