尧图精选

样板代码生成平台HagiCode Soul:架构设计与落地经验分享

🕒 发布时间:2026/9/7 17:01:22 📁 来源:尧图网络
样板代码复制粘贴到第十次的时候我终于受不了了。每接一个新项目都要把控制器、服务层、DTO、路由注册、基础配置这些东西从一个仓库搬到另一个仓库搬完之后还要逐个改包名、改目录名、改接口前缀稍微漏掉一个引用启动起来就是一片红。HagiCode Soul 这个项目就是从这个很俗气的痛点里长出来的。一开始它只是我笔记本里一个会提问的命令行小脚本后来慢慢长得越来越大从单机工具变成了有多用户、有权限、有版本管理、有插件机制的独立平台。这篇文章不做功能演示只把整个演进过程里的技术决策、架构拆解、踩坑记录摊开来讲给正在做类似工具类平台的朋友一个可参考的路线图。1. 需求萌芽样板代码带来的真实痛点1.1 当时我为什么想写 HagiCode Soul 的雏形2019年前后我经常需要快速搭建内部服务。所谓“快”其实一点也不快。表面上看新开一个仓库只需要几分钟但真正要动手写业务之前那套祖传的工程骨架就得磨掉半天把老项目的目录结构复制过来用编辑器全局替换所有出现的包名再手动清理掉上一项目遗留的公共代码、无用依赖、过期配置。最难受的是你不知道哪一次替换会漏掉一个不起眼的字符串等到编译期才一脸懵地看到几十个错误。这种“复制-替换-清理”的模式本质上是在用人工执行一台本该由程序完成的机器。而且每个项目之间并不是完全相同的有些项目要接消息队列有些项目只要纯HTTP服务有些项目还要带定时任务。复制过来的老骨架里残留着大量不需要的模块删又不敢删留着又碍眼日志里到处都是跟自己无关的初始化信息。我意识到真正的问题不是“能不能再复制得仔细一点”而是“骨架代码里哪些部分是可以参数化的”。一个工程模板有稳定的结构有不稳定的变量。稳定的部分是目录、基础依赖、生命周期代码不稳定的部分是项目名、模块名、端口、数据库连接前缀、是否启用某些中间件。只要有办法把这两类东西分开再在生成时把变量填进去事情就能自动化。1.2 第一版只是一个“会问问题的脚本”第一版HagiCode Soul的前身真的非常朴素。我把它设计成一个交互式命令行工具启动后它会依次问你几个问题项目名是什么用哪个模板要不要带Docker Compose数据库选型是什么答案收集齐后脚本把固定模板文件和用户输入合并渲染到目标目录。当时用了三个关键依赖第一个是模板引擎选的是 Handlebars因为它语法足够简洁前后端同学都容易看懂第二个是交互式命令行库 Inquirer几乎不用配置就能做出选择项和输入框第三个是用递归遍历模板目录把每一个文件都丢进渲染管线。整个工具只有一个核心函数大概思路是这样的async function generateProject(options: ProjectOptions) { const templateDir resolveTemplateDir(options.template); const files await walk(templateDir); for (const file of files) { const relativePath file.replace(templateDir, ); const content await readFile(file); // 把 {{ projectName }} 这类占位变量全部换成真实值 const rendered renderTemplate(content, { projectName: options.projectName, packageName: toPackageName(options.projectName), enableMq: options.enableMq, }); const outputPath join(options.outputDir, relativePath); await writeFile(outputPath, rendered); } console.log(项目已生成: options.outputDir); }这套脚本在本地跑起来特别爽。十几秒就把以前要折腾半天的骨架搭好了错误还更少。但用到第四五个项目时新的问题又浮出来了。1.3 为什么“代码生成”是表象“规则沉淀”才是本质脚本用了一段时间后我发现真正值钱的不是那一段渲染代码而是背后沉淀下来的模板规范和命名规则。同一个项目名在不同场景下需要不同形态要生成 Java 类名得转成大驼峰要生成包名得转成小写点分格式要生成资源路径得转成中划线格式这些转换规则分布在模板各处一旦某个规则变了就要把模板全部人工同步一遍。于是我把“变量来源”整理成一条清晰的链路。用户输入是原始值系统要做两件事一是派生变量比如 autoTypeName、autoPackageName二是校验合法性比如不允许出现中文包名、不允许以数字开头的资源路径。模板里只消费系统处理后的上下文不直接消费用户原始输入。这个抽象让模板的稳定性提高了一大截也让我第一次感受到平台的本质是把规则沉淀下来供后续反复消费。这其实就是 HagiCode Soul 的平台化起点。当规则越来越多、使用的人从我自己变成团队内几个同事时命令行工具就不再够用了。它缺少一套共享的模板仓库缺少谁在什么时候用了哪个版本的记录更缺少让非技术同学也能看得懂的配置入口。2. 平台架构设计把“脚本”拆成可持续演进的系统2.1 分层设计与模块边界平台化的第一步是重新梳理边界。如果直接把原来的 CLI 外面包一层 Web 壳子短期能跑但长期会变成一个到处漏风的大泥球。HagiCode Soul 在架构上从一开始就分裂成了四个边界清晰的模块接入层、业务层、执行引擎、存储层。接入层的职责是受理用户请求可能是网页点击也可能是 API 调用将来还可以接 IDE 插件业务层处理项目定义、模板版本、用户权限、生成记录执行引擎只做一件事就是把“一个模板”和“一份上下文参数”渲染输出成“一份完整项目”存储层则管住元数据、模板文件、生成产物和操作日志。每一层之间通过明确的接口通信不允许跨层调用。这里最容易被忽略的边界是“模板文件存储”和“渲染执行”的分离。模板文件不能直接放在业务服务器的本地目录里因为平台一旦有多副本部署每个实例看到的模板文件可能不一样。HagiCode 的解决方案是把模板文件统一收归到对象存储数据库只记录文件的地址、版本号和哈希值渲染时按版本号从对象存储拉取固定快照。这样既保证多实例一致性也天然支持版本回溯。2.2 技术选型为什么先模块化单体而非微服务我见过太多开发者在项目初期就把服务拆成用户服务、模板服务、渲染服务、消息服务结果一个人维护五个服务部署一次要编排半小时数据还分散在不同库里查一个问题要连三套库。HagiCode Soul 从一开始就定了原则先做模块化单体内部按领域拆分模块用 TypeScript 统一上下文结构所有模块跑在同一个进程里。模块化单体的好处非常明显。首先调试简单一个进程里谁调用谁一目了然其次事务边界清楚一个请求涉及模板、项目、记录三个模块时可以直接在同一个数据库事务里完成再次部署成本极低初期可以只需要一个应用服务加一个数据库。等到将来某条链路确实成了瓶颈比如渲染任务大量占用 CPU再把它单独抽取成 Worker 进程也不迟。技术栈选型上我坚持用 TypeScript 全栈。后端用 Node.js 生态前端用 Vue3 加 TypeScript。选择 TypeScript 的核心原因是可以把前后端共享的“上下文参数类型”和“模板变量协议”定义成同一套类型包。这样前端传了缺字段的参数编译期就会报错比靠后端接口文档校验要可靠得多。存储上初期用了 PostgreSQL因为项目元信息、模板版本、权限这些关系型数据天然适合表结构而模板文件内容放在对象存储更划算。2.3 元信息、模板与生成结果三者的数据模型先说一个容易踩的坑很多人会把“模板”的表设计得很复杂试图用一张表把一个模板里每个文件的路径、渲染类型、变更历史都存进去。这样设计到后期几乎必然会痛不欲生因为模板的内容是无规则的文本组合硬拆成结构化字段属于过度设计。HagiCode Soul 的实际做法是把模板当成一个不可变的版本包来管理。表结构大致可以理解为三个层次模板库templates记录名称、描述、可见范围模板版本template_versions记录文件在对象存储的地址、版本号、变更说明、创建人生成记录generation_records记录哪一次生成用了哪个模板版本、传入了哪些参数、最终输出的文件包地址。这套模型的好处是每次生成都能精确复现。CREATE TABLE template_versions ( id BIGSERIAL PRIMARY KEY, template_id BIGINT NOT NULL, version_no INT NOT NULL, file_bucket TEXT NOT NULL, file_key TEXT NOT NULL, file_hash TEXT NOT NULL, created_by BIGINT NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), note TEXT, UNIQUE(template_id, version_no) );生成记录是另一个容易被忽略的资产。我强烈建议自己做平台时无论如何都要保存每一次生成的参数快照。哪怕用户只是随便点了试一下也要记录。因为用户如果拿着生成产物去部署了后面发现某段代码有问题他最先问的就是“我当时生成的时候到底提交了什么参数”。没有记录这个锅就很不好定位。3. 从“自己用”走向“独立平台”的三次关键改造3.1 第一次改造模板中心从“本地目录”变成“独立仓库”CLI 阶段模板以一个文件夹的形式存放在本机某个路径下。到了 Web 平台阶段问题就很明显了用户不可能跑到服务器上改模板模板也不能只存在于服务器某块硬盘里。我需要做出来“模板中心”让用户能在线查看、创建、修改模板。这个阶段的模板中心本质就是一套版本化的文件管理工具。用户在界面上新建一个模板版本系统把一段内容或一个 zip 包提交到对象存储计算 SHA256 哈希再写入数据库。修改模板时必须生成新版本旧版本继续推给已经使用它的项目生成记录引用不会被覆盖。我故意让“删除”这个操作的路径变得非常深因为很多项目生成完之后半年甚至更久还会要重新生成一次模板版本一旦丢失用户拿什么来复现当年的生成结果这个痛点我在设计早期就觉得不能妥协。3.2 第二次改造租户隔离与权限模型当用户从“我”变成“我的团队成员”再变成“平台上的多个项目组”时权限就是一道绕不过去的坎。HagiCode Soul 的资源隔离模型没有走“一个租户一套Schema”的复杂方案而是用“空间”这个概念做逻辑隔离。每个空间拥有自己的模板库和项目记录用户可以被邀请进多个空间每个空间内可以分配管理员、开发者、只读访客三种角色。逻辑隔离方案在数据量大、安全要求高的时候确实不如物理隔离但做独立平台的起步阶段逻辑隔离最简单也最够用。我更看重的是访问控制层面能保证一件事一个模板的生成参数只对同空间成员可见空间的模板顶多被其他空间因“公开模板”而被引用但引用方拿到的只是一个只读副本不会看到原始编写人的私有上下文信息。权限校验统一收敛到一个中间件里任何接口在进入业务逻辑前先完成“用户-空间-资源-操作类型”的四元组校验。3.3 第三次改造开放自定义钩子模板不再局限于静态文件默认情况下渲染流程是“模板文件 参数 - 输出”。但真实业务里用户总会提出一些模板表达不了的需求比如生成完代码之后要往某个内部系统注册一下项目元信息或者要按特殊的规则跳着生成某些文件。为了不把这类需求变成一次次改代码、发版HagiCode Soul 设计了钩子机制。设计上用三段式钩子覆盖完整生命周期before_render 允许用户提供自定义的参数增强逻辑after_render 允许用户裁剪或补充生成文件after_finish 用于通知外部系统。用户可以在模板配置里写一段自定义函数但这段时间要注意钩子逻辑是平台方执行安全风险比较高不能允许任意代码直接跑在服务主进程里。方案是把钩子脚本放进受控的沙箱执行器里并限定运行时间上限和可访问资源避免出现用户故意或无意写了一个死循环就把整个平台拖垮的情况。这段演进其实已经是在做“平台生态”了。开放能力能不能兼顾安全往往是工具型产品能否独立存活的分水岭。4. 完整实操链路一次项目生成的全过程拆解4.1 配置项目元信息用户进入 HagiCode Soul 创建新项目时第一步是填写一份“项目元信息表单”而不是直接选模板。表单里的字段在设计模板时就已经定义好了模板可以有自己的一套“入参协议”。这样做的好处是模板作者能主动声明需要哪些输入参数并且可以给每个参数加上校验规则和默认值。例如一个“Spring Boot 基础服务模板”的入参协议可能是这样{ projectName: { type: string, required: true, pattern: ^[a-z][a-z0-9-]*$, description: 项目名只能包含小写字母、数字和中划线 }, basePackage: { type: string, required: true, default: com.example }, features: { type: array, items: { enum: [mq, redis, scheduler] } } }到了表单提交时前端会动态渲染对应的控件后端收到后先做协议校验再送入衍生值计算模块。例如 projectName 传进来是 order-center系统自动生成这些派生变量给模板用packagePath/order/centerclassNameOrderCenterupperUnderlineORDER_CENTER。这一步是 HagiCode Soul 和普通文本替换工具拉开差距的地方它真正做到的是按语法规则计算派生值而不是简单替换字符串。4.2 编写一份底层模板模板文件里不是只会出现 {{ projectName }} 这样的简单占位符。HagiCode Soul 渲染引擎支持条件占位、循环区块、局部文件引入。比如只有用户勾选了启用消息队列时pom.xml 里才会出现对应的依赖而控制器文件里也才会加上对应的注解。以生成一个 Spring Boot 项目的 application.yml 片段为例server: port: {{ serverPort }} spring: application: name: {{ projectName }} datasource: url: jdbc:{{ dbType }}://{{ dbHost }}:{{ dbPort }}/{{ projectName }} username: {{ dbUsername }} password: {{ dbPassword }} {{#if enableRedis}} redis: host: {{ redisHost }} port: {{ redisPort }} {{/if}}模板语法上我们复用了 Handlebars 风格但渲染引擎在底层做了一层包装让它可以自动从数据库读取 Schema 定义、预加载上下文。如果你在搭建自己的平台这一步不必自己写词法分析器用成熟的模板引擎做二次封装是最快的路线精力应该花在“清洗用户输入”和“转换派生变量”上。4.3 渲染引擎的执行流程一次生成任务的执行流程不是“一次性把所有文件读进内存再写回磁盘”这么简单而是有严格的阶段划分。HagiCode Soul 把执行链拆成七个阶段加载模板清单、拉取模板文件、合并上下文、渲染文件内容、执行差异合并、生成产物包、异步通知用户。加载模板清单时渲染引擎会读取模板根目录下的 manifest 文件。manifest 里声明哪些文件需要渲染、哪些文件是纯二进制直接复制、哪些文件在目标位置已经存在时应该采用什么策略。这个设计很关键它把“模板作者对目标代码库的了解”显式化而不是靠引擎自己猜。files: - path: {{ projectName }}/pom.xml type: render conflictStrategy: overwrite - path: {{ projectName }}/README.md type: render conflictStrategy: keep-both - path: {{ projectName }}/docs/arch.png type: copy conflictStrategy: skip执行渲染时按文件类型分发到不同处理管线。普通文本文件走文本渲染管线图片和压缩包等二进制文件走“直接复制”管线避免因为按 UTF-8 读文本文件导致二进制内容损坏。整个生成脚本会尽量保持节点的幂等性同一份模板同一个参数反复生成两次产物文件的内容完全一致这样用户才能大胆做重生成或增量更新。4.4 生成后的差异合并与收尾动作用户拿到生成产物后在真实项目里还会继续往里面写自己的业务代码。这时有一个比较现实的矛盾如果模板升级了用户希望把模板新版本里修改的骨架代码同步到自己的项目里但又不能把自己后续写的那堆业务代码覆盖掉。HagiCode Soul 给出的解决思路并不完美但非常实用同一个应用内的生成支持“增量覆盖”只对模板管理过的文件做差异合并用户自己创建的新文件不会被动。合并时检测到目标文件已经存在则比较文件头部标记。如果文件是在旧版本模板中生成、中途被用户改过系统会把变更列出来让用户人工确认如果没改过直接无条件覆盖。这条策略在真实使用中能覆盖绝大多数场景。收尾动作方面平台会按模板配置执行一段可选的“post_command”。比如自动执行 npm install、git init、格式化代码等等。这里有两点经验命令必须带超时时间默认最好不超过六百秒否则某个依赖源卡住会让整个任务占着 Worker 不释放命令里如果要用到外部服务凭据必须通过平台的密钥注入能力传入环境变量不能明文写在模板里。5. 性能优化与稳定性实战5.1 模板渲染遇到的性能瓶颈早期 HagiCode Soul 处理一个两百个文件左右的小型模板任务时长经常会在几十秒的量级。查了一段时间日志发现瓶颈主要出在两个地方循环中有大量同步文件读写频繁小文件写入导致磁盘 IO 成为瓶颈大量文件重复编译模板解析阶段没有缓存一个工程有四十个文本文件每个文件里都有好几处依赖同一个子模板的嵌套语法解析引擎就反复把子模板编译了四十次。还有一个非常隐蔽的性能问题出在上下文合并上。有些用户提交的项目描述文本特别长里面有大量换行和特殊字符按普通文本替换时会触发模板引擎里的一些转义逻辑白白消耗很多 CPU。后来在接入层对用户输入做了预处理长文本统一截断特殊字符按白名单过滤性能立刻提升了一些。5.2 缓存、并发与流式生成针对上面的瓶颈我做了三件事。第一给模板引擎增加两级缓存第一级缓存子模板编译后的函数第二级缓存“同一个模板路径同一段上下文”对应的最终渲染产物不过二级缓存只在同一次任务里存活避免占用太多内存。第二把单文件读写改成多文件并发给文件 IO 增加了并发送量控制经验上并发数设为 CPU 核心数的两倍最合适太高反而会触发磁盘排队。第三抛弃“全量载入内存再统一生成 zip”的做法改成流式输出。先把每个文件按目录分片渲染立即写入临时目录等所有文件都处理完再统一流式压缩。这样极大降低了内存压力。优化之后一个约一千二百个文件的模板从提交到产物包下载完成压缩到十几秒以内比起初有八十多秒的耗时体验已经完全不同了。为了让你能顺畅地定位性能问题我给常见的排查项做了张速查表现象可能原因排查方式生成任务长时间挂起模板引用了外部资源但拉取超时查看任务日志中文件拉取耗时内存持续上涨某文件过大且被整段读入内存检查是否有超大单文件设为纯复制高峰期任务排队严重并发 Worker 数量配置过低监控队列长度与 CPU 使用率某些模板渲染快某些很慢公共子模板重复编译检查子模板缓存命中率5.3 部署升级与安全审计HagiCode Soul 的运行形态经历过一次重要切换。初期是单机部署代码、存储、数据库全在一台服务器上一旦升级模板引擎版本或依赖库就必须停机几分钟。后来切成了标准容器化部署应用服务可以多副本对象存储独立PostgreSQL 独立。前端静态文件走对象存储加 CDN后端无状态化之后滚动发布就只要逐个摘流量、更新副本、重新挂载用户基本无感知。安全方面有三条底线我建议类似平台一定不要省。第一是操作审计日志谁的什么操作、修改了哪个模板版本、生成过哪批项目都要可以追溯第二是权限越权防护不能只在前端隐藏按钮所有资源访问接口必须做后端权限校验特别是通过 ID 遍历他人资源这种越权路径第三是钩子脚本的隔离执行自定义代码默认不能访问平台内部网络的元数据接口否则脚本一旦被外部攻击者利用危害会被放大成整个平台的数据泄露。6. 避坑实录常见问题与排查速查6.1 四类高频故障及处理方案生成类平台走向独立使用后遇到的高频问题无非这几类这里挑最典型的记录一下也算给后来人探路。第一类是模板变量漏渲染。表现是生成出来的代码里还有 {{ xxx }} 原样残留一般是模板文件里写了 undefined 变量或者变量名拼错没有在上下文里注入。排查时直接在渲染日志中打开“未渲染占位符警告”就能准确定位到文件路径和变量名。平台做法是任何文本文件里只要渲染完还存在未替换的占位符结构系统就将该次生成标记为 warning而不是默认成功。第二类是跨平台路径分隔符问题。模板清单里的路径在 Windows 上显示为反斜杠到 Linux 执行环境就变成文件找不到。处理方式统一为模板路径只记录相对路径格式并且内部全部转成 POSIX 风格/ 分隔到输出目标目录时再用系统相关分隔符拼接。第三类是并发生成时的空间资源占用问题。多个用户同时生成大型项目时磁盘空间被临时文件打满。后来给每个空间设置了可用的临时磁盘配额任务开始前检查配额不足时直接把任务挂起而不是硬跑。第四类是用户生成代码和自己手写代码冲突。再智能的差异合并也无法替代人工确认因此在产物包里额外生成一份“.hagicode/report.html”文件把哪些文件被覆盖、哪些被保留、哪些有冲突标得一清二楚。不少用户在群里反馈这个报告比生成的代码还有用。6.2 模板协作中的流程问题技术问题好解决人的协作流程反而更容易让平台翻车。刚开始让整个团队都能编辑模板后经常出现一种情况有人改了公共模板里的一个公共变量影响了所有正在使用旧版本模板的存量项目。我们很快立了一条规矩改模板必须发新版本旧版本只修 bug不引入新的结构性变化涉及模板依赖的公共能力变更需要在更新说明里写清楚迁移影响范围。模板作者和项目使用者的角色也是一个问题。最早期我们对任何空间成员都开放模板编辑权限后来改成只有模板管理员能发布新版本开发者可以创建草稿但是不能直接发布。这个调整看似限制了不少自由度反而让模板质量大幅提升因为草稿和发布之间多了一道 review 环节很多低级失误在发布前就被拦住了。6.3 独立平台最容易被忽略的三个底线第一个底线是“可复现性”。只要平台承诺生成结果是确定性的那么同一个模板版本、同一份参数任何时候生成出来的文件内容必须完全一致。哪一天插入了一个不稳定的时间戳导致文件本身哈希变化用户的自动化校验就会开始报警。因此渲染上下文里默认禁止注入当前时间除非模板作者显式声明。第二个底线是“可回退性”。用户误操作删除整个项目时不能真的从数据库里物理删除。HagiCode Soul 的做法是软删除把记录标记为 deleted并且保留三十天的恢复窗口。模板版本同样是只允许废弃不允许删除因为可能有一千个历史生成记录引用着旧版本。第三个底线是“防滥用”。如果平台允许用户提交模板并让他人使用不可避免会有人上传带恶意代码的模板比如在 post_command 里执行 curl 外传数据。为此要建立模板内容安全扫描链路对上传模板的压缩包进行静态扫描至少检查文本文件是否含有明显的可疑下载执行指令对命令类配置必须做白名单限制不允许模板配置里直接执行任意 shell 语句。7. 复盘与下一步平台从“能跑”到“好用”的关键体会7.1 回看演进历程哪三个决定做对了第一个决定是把“模板版本不可变”当作底线执行。没有这一步后面所有用户项目的可复现、可审计、可回溯都无从谈起。即使很多次有人希望直接改旧模板“图省事”我还是坚持了宁可发布一个新版本也不动旧版本。第二个决定是早早就做了生成参数快照。也就是用户提交参数的那一刻系统就把完整上下文存成 JSON 快照。后来排查用户问题时我们可以直接告诉他是哪一次生成、用的哪个模板版本、传了哪些参数。这在“找问题现场”的时候价值极大省掉了大量来回询问。第三个决定是模块边界分离得比较干净。执行引擎自始至终不感知 Web 层的登录态只接收一个包含 templateVersionId 和 context 的任务对象。这为后来接入 IDE 插件、Open API 打下了基础。其他模块也沿用了同样逻辑接口只做自己分内的事避免不必要的耦合。7.2 哪些事当时应该更早做如果现在让我把演进过程重走一遍有两件事我一定会往前放。第一是示例模板和端到端测试。早期我们靠人工点页面来验证渲染链路是否正常效率特别低。后来把每个重要模板都配了一个最小化的“示例项目元信息”纳入自动化回归集每次平台代码改动后跑一遍能保证基础渲染流程不坏。这套东西应该在平台还没复杂起来的时候就搭好。第二是模板生态的治理公约。刚开始允许任何人创建模板结果模板中心出现了大量重复度极高的模板有的甚至只是把另一个模板改了名字。平台自身应该更早提供“模板复用”和“模板收藏”的数据指标引导社区向高质量模板集中而不是让质量差的模板淹没好模板。事后看这不是一个技术问题但直接影响平台的生命力。7.3 如果让我从零重新开始会怎么走这个项目如果重新做一遍我不会第一步就动手写渲染引擎甚至不会先写代码。我会先强制自己定义清楚三样东西模板入参协议、模板版本发布规范、生成结果的可复现标准。这三样是灵魂代码只是表达。很多项目看起来功能很多但实际上它没有“规则”只是把一堆文件搬到另一堆文件里用户拿到手之后依然要靠人工去理解、补丁、修葺那是因为作者把整个系统的定位搞错了。做技术平台和写业务系统有一点很不一样业务系统的交互对象是最终用户平台类工具的交互对象里还含着一群“构建者”。你的边界设计得是否清晰、规则约束是否一致、版本处理是否可回退构建者会用脚投票。对于 HagiCode Soul 这套模板生成平台的实践我现在最大的体会是一个好用的工具不是“开发”出来的而是“长”出来的。每隔一段时间回看它都能干净地吸收掉新的需求又不破坏已经跑通的链路。那些你在架构设计时多投入十分钟想清楚的地方往往会在未来省下十个小时的返工。如果你也准备做一个生成器、脚手架或者低代码平台我能给出的核心建议始终是这句先把上下文模型和版本协议捏稳再谈渲染效率与交互体验。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →