“仅 6 个月,我们造出了比 10 年老系统还多的技术债……”

文章讲述了一个开发团队在构建房产评价平台时,为了追求开发速度,错误地决定将“房产观点(Opinion)”和“公寓信息(Apartment)”这两个业务实体共用一张数据库表。随着业务迭代,该表充斥着大量可选字段和逻辑判断标识,导致代码逻辑模糊、维护困难。作者对比了一个拥有十年历史的老系统在处理新功能时坚持解耦、独立服务的做法,强调技术债并非源于技术选型或前人留下的烂摊子,而是源于开发者日常工作中每一个“看似合理”的微小决策。文章警示开发者应重视业务边界,避免为了短期效率而牺牲长期的系统可维护性。


2026-01-30 18:54 江苏

“仅 6 个月,我们造出了比 10 年老系统还多的技术债……”

技术债就藏在我们每天的每一个开发决策里。

“仅 6 个月,我们造出了比 10 年老系统还多的技术债……”

【CSDN 编者按】在软件工程里,技术债往往不是一次“重大失误”的产物,而是由无数个“当时看起来还不错”的小决定慢慢堆出来的。本文作者就用了一个极其真实的案例,讲清了一个很多团队都会踩的坑:为了省事、为了快、为了“先跑起来”,不断模糊边界、复用模型,最终让数据结构和业务语义一起失控。

原文链接:https://superkacper4.github.io/portfolio-2023/blog/technical-debt-everyday

作者 | Kacper Kuczewski       翻译 | 郑丽媛

出品 | CSDN(ID:CSDNnews)

六个月前,我们做了一个看起来非常小、也非常“合理”的决定:不新建数据库表

原因很简单,系统里的两个业务实体“看起来足够相似”,于是我们直接把它们塞进了同一张表中——谁也没想到,就是这个决定,让我们在短短半年里,造出了比我日常维护的 10 年老系统还要多的技术债。

接下来,就说说这个坑是怎么一步步挖出来的

“仅 6 个月,我们造出了比 10 年老系统还多的技术债……”

“仅 6 个月,我们造出了比 10 年老系统还多的技术债……”

一张表,拖垮整个系统

最初,我们的应用逻辑非常简单:用户可以对房产添加观点(Opinion)。当时的数据模型大概长这样:

    model Opinion {
      id         String    @id @default(cuid())
      building   Building? @relation(fields: [buildingId], references: [id])
      buildingId String?
      pricePerSquareMeter Int
      rating  Int?
      message String?
      area    Float
      opinionDate DateTime @default(now())
      createdAt DateTime @default(now())
      updatedAt DateTime @updatedAt
    }

    你可以看到,这里面已经存在一些明显是“房源”和“观点”都能共用的字段,比如:buildingId、pricePerSquareMeter、area,一切都还算正常。

    “仅 6 个月,我们造出了比 10 年老系统还多的技术债……”

    “再加一个字段也没什么吧?”

    问题出现在我们准备引入公寓(Apartment)实体的时候。

    我们发现,当前的 Opinion 表缺少一些对“房源”来说非常关键的字段。但由于当时评价和公寓信息都支持注册用户提交,我们就给表加了用户关联关系,功能覆盖依然完整,一切看起来都还不错。

    于是我们继续迭代,很快又发现缺了房间个基础字段,那就加一个numberOfRooms;接着又加了floor(楼层)、height(层高)……从技术角度看,这些字段放在 Opinion 表里并不会报错;但从业务角度看——它们对“观点”毫无意义

    “仅 6 个月,我们造出了比 10 年老系统还多的技术债……”

    慢慢地,事情开始失控

    久而久之,整个表的一半字段都成了可选属性(?),这种设计也让可选属性遍布了整个应用的代码中。

    从数据层面看,这张表已经乱成一锅粥,最后我们不得不往表里加一个 isApartmentOpinion 的标识字段,用来区分这条数据到底是公寓信息还是房产评价,想想都觉得无奈。

    而从代码层面来说,问题更严重:开发者根本无法快速判断一个 Opinion 对象到底是公寓信息,还是单纯的房产评价。

    这一操作直接埋下了巨大的技术债。但最可悲的是,在很长一段时间里,所有人都觉得当初“合表”的决定是合理的——直到随着业务迭代,这个决策的弊端才被无限放大,变得无可挽回。

    “仅 6 个月,我们造出了比 10 年老系统还多的技术债……”

    背景补充:一个 6 个月的成熟应用

    这里提一下我们的应用背景,这是一个基于 Next.js 开发的单体应用,虽然只有 6 个月的历史,但已经是一个成熟的产品,甚至对接了外部支付系统。

    它不算超复杂的大型应用,但绝对也不是小打小闹的 Demo。

    这个产品的初衷,是做一个房产市场的评价和价格交流平台,帮助用户做房产决策——你可以把它理解为:“房地产版的 Glassdoor”(职场评价平台)。

    也正因如此,当我们后来想在应用中加入房产广告功能时,系统里已经有了那张 Opinion 评价表,这也是当初我们选择“合表”的重要原因。

    “仅 6 个月,我们造出了比 10 年老系统还多的技术债……”

    鲜明对比:10 年老系统的正确操作

    最近,我们在维护一个10 年的老系统时,要开发一个“优惠券”新功能,初期也和往常一样,打算直接在单体应用里开发,把所有相关数据都塞进老系统的数据库。

    但很快我们意识到,这个功能迟早要和公司的商城微服务集群做外部集成。

    于是我们停了下来:没有“先凑合”,也没有想着“反正能跑”,我们把整套逻辑直接拆出去,做成了一个独立服务

    这个做法确实增加了开发工作量,也导致了功能上线延期,但换来了一个最重要的结果:优惠券业务有了唯一的数据,后续的维护、集成、迭代都有了清晰的边界,从根本上避免了技术债的产生。

    “仅 6 个月,我们造出了比 10 年老系统还多的技术债……”

    经验教训:技术债从不是凭空出现的

    这次的经历让我彻底明白:技术债从来都不是凭空产生的,而是我们每天在开发中,一步步亲手造出来的。

    技术债的产生,从来不是因为开发者偷懒、没时间重构,或者选了什么小众的技术栈——更多时候,是我们在开发中做了那些看似合理的小决策,而这些决策在业务迭代中不断发酵、滚雪球,最终演变成无法收拾的大问题。

    比如那句耳熟能详的“后续再重构(TODO: Will refactor it later)”,还有 “这两个实体看起来差不多,不如共用一套逻辑吧”,最后都会让代码里充满各种判断分支(if),变得臃肿且难以维护。

    最后,我想和所有开发者共勉:请重视技术债,并且一定要意识到,技术债就藏在我们每天的每一个开发决策里。它从来都不是 “前任开发者留下的烂摊子”,而是我们自己埋的坑,是你,也是我。

    终有一天你会发现,自己对着屏幕抓耳挠腮想要调试的糟糕代码,正是当初自己写的。开发之路多坑,愿我们都能保持清醒,积极面对,少埋坑,多填债。

    推荐阅读:

    2026 奇点智能技术大会上海站官宣!解码AI Agent、世界模型与氛围编程等新范式

    为没有Linus的一天做准备!Linux社区敲定接班预案:若维护者不愿干就立刻定替代人选,这事绝不能拖

    多模态和编程能力可以兼得吗?Kimi新模型K2.5实测

    未来没有前后端,只有 AI Agent 工程师。

    这场十倍速的变革已至,你的下一步在哪?

    4 月 17-18 日,由 CSDN 与奇点智能研究院联合主办「2026 奇点智能技术大会」将在上海隆重召开,大会聚焦 Agent 系统、世界模型、AI 原生研发等 12 大前沿专题,为你绘制通往未来的认知地图。

    成为时代的见证者,更要成为时代的先行者。

    奇点智能技术大会上海站,我们不见不散!

    图片

    阅读原文

    跳转微信打开

    AI 前线

    50 万行代码不敢交给 AI?TypeScript 之父直言:它就像是个“高级复读机”

    2026-1-31 18:06:11

    AI 前线

    OpenAI CFO 摊牌:算力即营收,而 90% 的企业正被卷死在“能力鸿沟”里

    2026-1-31 18:06:20

    0 条回复 A文章作者 M管理员
      暂无讨论,说说你的看法吧
    个人中心
    购物车
    优惠劵
    今日签到
    有新私信 私信列表
    搜索