给技术团队定规范,为什么 90% 最后都变成了走形式?

节日刚来,公司把隔壁业务线的一个核心骨干提拔成了前端组长。

新上任,他花了整整一个晚上,在内网的 Confluence 上写了一份👉 《前端团队研发协作与编码规范》

为了体现专业度,他还特意把文档链接发到了全员群里,并且配置了非常严格的 ESLintHusky 提交拦截。

我随手点开那份规范看了一眼,里面有一段关于网络请求和 TypeScript 类型的规定,写得非常理想主义😢。

我直接把原文档的部分片段,给你们摘录出来👇:

4.1 严禁使用 any 业务代码、公共组件、工具函数、接口定义、状态管理中,均严禁出现 any
4.2 严禁使用 unknown 规避 any unknown 仅允许在底层工具函数中使用,业务代码中不得使用 unknown 作为后端数据兜底类型。
4.3 接口出入参必须完整定义 所有后端接口必须定义请求参数和响应参数。

当时开会在投影仪上过这份文档时,大家纷纷鼓掌说好,觉得咱们的代码终于能摆脱草台班子的气质,向大厂开源规范看齐了😁。

结果晚上临近发版,我帮他们组 Review 一段紧急合入的业务代码时,差点笑出声。

理想中的规范是一尘不染的,但现实业务里的代码全是泥巴。我给你们看看,为了躲避这段严苛的规范,一线开发兄弟在提测倒计时的重压下,写出的代码:

typescript 复制代码
// 后端老哥下午临时改了表单的返回结构,把原本的数组改成了一个逗号分隔的字符串
// 前端兄弟急着提测,但又被 Git Hook 死死卡住不让提交
// 于是他为了绕过严禁使用 any的规范,写出了这种极其糊弄的声明:

type MagicType = unknown; // 高级 any,完美骗过简单的 ESLint 扫描

interface UserFormRes {
  userId: number;
  // 先 unknown 占位
  dynamicExtData: MagicType; 
}

const UserProfile = () => {
  const [data, setData] = useState<UserFormRes | null>(null);

  const renderPrivilege = () => {
    // 规范要求:必须用可选链,必须做类型兜底
    // 于是业务层出现了一坨安全转换代码:
    const extStr = (data?.dynamicExtData as string) || '';
    const list = extStr.split(',').filter(Boolean);

    // 强行把 boolean 转换回后端有时候乱传的 0 和 1
    const isVip = (data?.dynamicExtData as any)?.is_vip == '1' || false; 

    return <div>{/* 渲染逻辑 */}</div>;
  }
}

再去翻翻那一天的 Git 提交记录。 原本规范里白纸黑字写着,Commit Message 必须是 feat(模块名): [JIRA号] 更新详情。 但大家被拦截规则搞烦了,又不想去查具体的命令格式,于是那一晚的记录里,满屏都是用快捷指令强行绕过的产物:

git commit -m "fix" --no-verify

看着新负责人在群里无奈地圈人,问大家为什么不按规范来,我其实非常理解他的挫败感的🤔。


现实问题

在这个行当混了这么多年,带过好几个大大小小的团队,我见过太多这种死在摇篮上的技术规范。很多人总觉得,只要规矩定得细,配上拦截工具,大家就会乖乖就范。

但这忽略了一个最致命的现实:前端是直接扛业务压力的最后一环。

当产品经理站在你工位旁边,问你为什么这个按钮点下去没反应?测试在群里疯狂 @你,倒计时还有半小时就要封板上线。这个时候,你脑子里只有一件事:把逻辑跑通,让报错消失。

这时候谁有空去翻那十几页的文档,查一查这个嵌套了四层、连后端自己都说不清楚的 JSON 结构,到底该怎么老老实实写成 Interface

当你的规范阻碍了同事准时下班,一线的开发者有一万种方法绕过你的各种 Lint 工具。

那么,既然这种事无巨细的规范行不通,团队代码是不是就活该变成垃圾堆?

其实也并不是。真正有效的前端管理,从来不是写小作文,而是抓大放小。

我现在带项目,从来不限制组员在具体的单个业务文件里怎么折腾。我不关心他是不是多写了几个毫无意义的 div 嵌套,也不关心他是不是为了图省事写了个 as any。那些东西虽然不优雅,但最多就是他自己几个月后接手时看着头疼,影响面可控。

但我会死守工程的底线边界。给大家看一个我平时做 Code Review 绝对会当场打回重写的真实案例:

javascript 复制代码
// 某个新人写在促销活动页面的代码
// 这种代码我一眼都不会让它合进主分支

useEffect(() => {
  // 错误 1:在业务组件里直接裸写 fetch,绕过全局请求实例
  fetch('https://api.nurverse.com/v1/get-discount')
    .then(res => res.json())
    .then(data => {
      setDiscount(data.price);
    })
    .catch(err => {
      // 错误 2:私自吞掉报错,不接入统一监控
      console.log(err); 
    });
},[]);

我不强制他定义完美的入参出参,但我绝对不允许他裸调接口和私自吞掉错误🫡。

因为一旦这个接口报了跨域,或者后端抛了 500,全局的 Axios 拦截器抓不到它,公司的统一监控大盘也收不到告警信息。到了第二天客诉找上门,全组人都要陪着他排查到底是哪个节点挂了,这就是在给整个团队挖坑。

我会让他老老实实改成这样👇:

typescript 复制代码
// 所有的网络层交互,必须走基础封装
import { http } from '@/utils/request'; // 统一的请求封装
import { reportMonitor } from '@/utils/monitor'; // 统一的监控上报

useEffect(() => {
  // 必须通过全局 http 实例调用,自动带上 Token、重试机制和全局拦截
  http.get('/v1/get-discount')
    .then(data => setDiscount(data.price))
    .catch(err => {
      // 非公共错误,业务侧拦截后必须手动上报大盘
      reportMonitor('Discount_Fetch_Fail', err);
    });
}, []); // 依赖数组根据实际情况补充

发现区别了吗?


别用规范去管人,用基建去管代码

管理技术团队,和敲键盘写代码是两套完全不同的逻辑。代码只要逻辑对,它就永远按预期执行,但人就不是🤷‍♂️。

任何一项技术规范的落地,本质上都是一次团队内部的习惯磨合。别再拿着那份十几页的文档试图去改造全组人了。砍掉那些脱离业务的伪需求,删掉那些让人抓狂的强制检查,去关注那些真正拖慢团队协作的链路瓶颈🫵。

当你的团队成员发现,你定的每一条规则,都是在帮他们少踩坑、少背锅、能准时下班的时候。根本不需要任何强制文档,你提倡的最佳实践,自然会成为大家的工作习惯。

这才是一个前端负责人,真正该有的技术管理品味。

你们说是不是呢?

![谢谢大家 (2).gif](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/6421fdd06322456eb7c6fa7594b54e7d\~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgRXJwYW5PbWVy:q75.awebp?rk3s=f64ab15b\&x-expires=1778812392\&x-signature=nowslLFsow3M1cbLuetDO%2BDrrg4%3D)

相关推荐
小番茄夫斯基2 小时前
Node.js 从零开发 MCP 服务:30 分钟上手,对接 Claude/Cursor 全流程
前端·mcp
LIO2 小时前
一套代码,多端并行——uni-app + Vue3 多端开发完全指南
前端·vue.js·uni-app
众创岛2 小时前
web自动化中的日志模块
java·前端·自动化
昼猫2 小时前
前端打印分页技术探讨与 PrintomJs 方案
javascript·浏览器
gCode Teacher 格码致知2 小时前
Javascript提高:点击飘忽不定的气球,气球爆炸并增加分数-由Deepseek产生
javascript·css·css3
是谁眉眼2 小时前
npm执行错误 但黑窗口node可以成功启动问题分析
前端·npm·node.js
费曼学习法2 小时前
React Hooks 闭包陷阱:为什么 useState 拿不到最新值?
javascript·react.js
ChalesXavier2 小时前
SSE(Server-Sent Events,服务器发送事件):从协议细节到流式处理实战
javascript
前端那点事2 小时前
干掉重复请求!Vue+Axios全局防抖节流封装,企业级开箱即用
前端·vue.js