[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-personal-site-feature-bloat":3,"article-neighbors-personal-site-feature-bloat":17,"article-related-personal-site-feature-bloat":39},{"id":4,"slug":5,"title":6,"excerpt":7,"category":8,"tags":9,"content":13,"publishedAt":14,"readingMinutes":15,"published":16},32,"personal-site-feature-bloat","个人项目如何规避「功能废墟」工程问题","功能越加越多，项目却越来越难改，最后重构比重写还贵——这是我踩过的\"功能废墟\"坑。","工程实践",[10,11,12],"个人网站","工程管理","维护","\"功能废墟\"这个词是我自己总结的：一个项目加功能加到最后，变成一堆拆不动的烂摊子——模块堆着没边界，废弃代码没人敢删，依赖版本乱七八糟，业务逻辑缠成一团。最后迭代停摆，bug 修不完，重构的成本比重写还高。\n\n我的个人站差点就走上这条路，好在及时刹住了。今天把这段教训写下来，对做个人项目的人应该都有用。\n\n### 为什么项目会变成废墟\n\n为什么会变成废墟？我复盘下来，原因不外乎三个。\n\n一是想到什么加什么。今天觉得该有个统计功能，明天觉得加个留言板不错，后天又想做个排行榜——没有一个明确的核心边界，功能越加越多，什么都想做，最后什么都不精。这背后其实是产品层面的失焦：没有想清楚\"这个产品到底为谁解决什么问题\"，功能自然就失控了。个人项目尤其容易犯这个毛病，因为没人给你把关，你既是产品经理又是开发，最容易\"自嗨式加需求\"。\n\n二是模块没有边界。通用逻辑、业务逻辑、页面逻辑混在一起，同一个请求缓存的逻辑在三个页面里复制了三份。某天要改缓存策略，只改了其中一个页面，另外两个漏了，bug 就出来了。这是软件工程里典型的\"重复代码\"问题——它让每一次修改都要改多处，漏一处就埋雷。更麻烦的是，当重复代码散落在各处时，你根本不知道\"这个逻辑到底有几份\"，改起来就像在雷区里走路。\n\n三是垃圾没人清理。废弃的接口没有调用方了，还继续占着代码、测试和文档；无效的依赖、冗余的组件长期躺在项目里，越积越多。这在行业里叫\"技术债\"——每写一行\"先这样吧\"的代码，都是在给未来的自己记账，利息会越滚越高。技术债最可怕的地方在于它是\"隐形\"的：你不去清理，它不会主动提醒你，但每次改动都会因为它的存在而变慢。\n\n### 我给自己定的几条规矩\n\n后来我给自己定了几条规矩，项目才算稳下来。\n\n第一，先想清楚这个站的核心是什么。我的核心就是三件事：展示技术文章、管理内容、沉淀数据。其他那些娱乐化、装饰性的功能，一律往后放，不在一开始就往里塞。这对应着软件工程里的 YAGNI 原则——\"你不需要它\"（You Aren't Gonna Need It）。不确定要不要的功能，先不做，等真的需要再加。这个原则看着简单，但真正做到很难——因为它要求你抵抗\"功能越多越有成就感\"的诱惑。\n\n第二，迭代顺序固定成\"先稳后扩\"。先把服务稳定性、数据安全、架构完整性做扎实，再谈加新功能。地基没打牢就往上盖楼，后面全是返工。个人站尤其要守住这条：内容站的核心资产是内容和服务稳定，不是花哨的功能。\n\n第三，公共能力一律抽成公共模块。通用的逻辑放公共层，业务模块各自独立，新增功能必须满足可复用、可维护、不侵入三个原则，不能哪哪都插一脚。模块之间用清晰的接口互相调用，谁依赖谁都明明白白，这就是\"高内聚、低耦合\"的实践。判断抽不抽的标准很简单：同一个逻辑出现第二次的时候，就该抽了——\"三次法则\"（Rule of Three）是业界常用的经验。\n\n第四，定期做\"大扫除\"。每个季度检查一次：接口还有没有调用方、组件还有没有被引用、依赖还需不需要、注释还有没有意义。该删的删，该合并的合并。这一步尤其重要——没有人会主动删自己写过的代码，但不删，垃圾就会越堆越多。\n\n### 一点工程层面的补充\n\n除了上面这些，我还想补两个技术细节。一个是测试：关键逻辑一定要有测试兜底，不然你根本不敢重构。个人项目可以不用追求覆盖率，但核心的接口、数据逻辑、异常分支，至少要有一层保护，这样\"大扫除\"的时候才敢动手。另一个是依赖管理：个人项目很容易依赖越加越多、版本越升越乱，最好固定一个主版本，升级前先在小范围验证。\n\n说白了，工程治理的核心就俩字：克制。个人项目的长期价值，靠的是架构稳定和内容质量，而不是功能数量。一个功能如果哪天没法下线，那最好一开始就别做。做个\"能一直改下去\"的站，比做个\"功能一大堆但碰都不敢碰\"的站，有价值得多。\n","2026-05-15T02:00:00Z",6,true,{"prev":18,"next":29},{"id":19,"slug":20,"title":21,"excerpt":22,"category":23,"tags":24,"publishedAt":28,"readingMinutes":15},3,"certificate-system-retrospective","去中心化数据存证系统｜业务拆解、技术选型、避坑复盘","存证系统真的需要区块链吗？我用哈希加时间戳加链式关联，给出了一个轻量答案。","项目复盘",[25,26,27],"密码学","Java","分布式系统","2026-03-20T02:00:00Z",{"id":30,"slug":31,"title":32,"excerpt":33,"category":8,"tags":34,"publishedAt":38,"readingMinutes":15},1,"build-a-reliable-personal-site","个人网站工程架构与长期可维护设计","这个站从零搭起来，分享我用 Nuxt 加 Spring Boot 把个人项目做成敢一直改下去的经过。",[35,36,37],"Nuxt","Spring Boot","部署","2026-07-20T02:00:00Z",[40,42,53],{"id":30,"slug":31,"title":32,"excerpt":33,"category":8,"tags":41,"publishedAt":38,"readingMinutes":15},[35,36,37],{"id":43,"slug":44,"title":45,"excerpt":46,"category":8,"tags":47,"publishedAt":52,"readingMinutes":15},24,"vue-request-cache-implementation","Vue 全局请求缓存工程级实现、过期策略与脏数据规避","给 Vue 项目加全局缓存时踩过的坑：过期策略、脏数据、并发去重，一篇讲清楚。",[48,49,50,51],"Vue","TypeScript","缓存","性能","2026-01-25T02:00:00Z",{"id":54,"slug":55,"title":56,"excerpt":57,"category":8,"tags":58,"publishedAt":60,"readingMinutes":15},31,"virtual-list-is-not-enough","前端性能优化：虚拟列表真实适用场景与完整优化链路","列表卡顿别急着上虚拟列表，先排查网络、数据、渲染整条链路，最后才是它。",[48,59,51],"虚拟列表","2025-12-10T02:00:00Z"]