文章分享——好代码 - 半点没用的话题

刚毕业面试的时候,总有面经告诉你,参与开源代码是多么多么贴金的体验,能极大提高拿到 offer 的概率。当时,一直觉得是因为开源代码非常优秀,能参与是很了不起的人。直到,我自己实际来维护开源项目。我才知道,这个世界的水平就是那么参差的,好代码从来不是一个产品能不能赚钱的决定因素,甚至不一定是重要的因素。

一份代码写完之后,会有不同的使用方关注不同的部分:

  • 开发者:需要长期维护,关注代码更细节的东西,比如命名、配置、日志、测试、模块化

  • 使用方:在意错误码、协议和插件,好用就行

  • SRE:关心监控告警,确保系统不会出事

  • 机器:负责运行代码,关注性能。性能是实打实的需求,不属于半点没用的范畴

命名

代码的命名就变量、函数。很多很多书籍都说过这块的内容。总结起来就一个点,能反馈它的实际作用。从这点来说,写代码和写一篇文章是一样的,你得上文提到过这个东西,给这个东西明确定义,下文才能引用。如果名字不清晰,就等于说根本没有给它一个明确的定义

比如,在 Get** 函数里面去更新一个啥的。这就不能准确地反映这个东西的作用。因为,我们阅读代码的时候,并不一定会一行一行去看,而是会先看个大概,特别是开源代码,动不动几十万行。

如果遇到函数里面功能比较多的情况,就考虑

  • 要拆吗 ? 如果能功能分开,就尽量分开。比如一个函数既要 Get 又要 Update,那就可以拆成两个。如果拆完以后发现它两经常一起用,就再组合一个

  • 拆不开就改函数名。我也会经常想一个函数叫 GetAndUpdate** 是不是太长了。但是,我研究生导师告诉我,内容决定形式,而不是形式决定内容。它就得这么长才能反映它的内容。而且,在比较优秀的开源代码中,也会用这种比较长的名字,它不会因为它很长,就读不懂

配置

为什么需要配置 ?基本上可以认为是低成本修改代码逻辑。这里的底层本就包括

  • 逻辑生效的成本。代码修改的话,需要走一整套上线逻辑,没准还要等火车。动态配置能够及时修改就生效

  • 修改的心理负担。配置相当于把一个逻辑从大量代码中抽取出来。无论是修改还是 review,心里负担都会减小

什么时候需要配置 ?一方面使用者希望有灵活的配置可以调整,另一方面我看到过配置超级多的项目,多到你都希望它不要提供这么多配置,给你看 捂脸。一般提供配置的地方,都是设计者觉得,可能会变的。

配置的方式,有很多,我接触过的,起码有:环境变量、文件、数据库、配置平台(比如 tcc)

具体用哪一种基本取决于

  • 安全。很久以前秘钥都是写在配置文件里的,因为它确实是非常低频修改的内容,但是现在都鼓励在平台上配置了。虽然,我不知道具体是不是有什么故事,但事情就是这么发展了

  • 配置的修改频率,以及部署的方式能不能支持到这样的修改频率。比如说,修改环境变量,在内场 tce 平台是一件比较简单的事情。但到我目前使用的自建环境,就需要打包 helm chart,发布版本才行

  • 用户相关的配置一般修改频率会高于,代码运行所需要的配置,一般会放在支持高频修改的地方。(我之前没有想过这个点,刚想到的。还需要更多验证)

日志

日志的复杂性,在很多书上是被低估的。日志打不打,什么时候打,打什么,都很有讲究。打多了,不说性能问题,找起来也麻烦。打少了,不够排查问题。有几个可以参考的思路

  • 配置文件,读完最好都打出来,有敏感信息的可以脱敏。真的遇到很多次,以为读的是 A 配置,实际跑的是 B 配置的情况
  • 出错的时候。那这时候显然不能不打了,不然就不知道为啥出错了
  • 逻辑不符合预期的时候。它不是一个错误,只是单纯,你觉得,它应该走到这个分支上来。这个时候,最好也打个点
  • 正常运行的时候。这个是最难的,非常依赖业务场景,特别是在高 qps 的场景,不打日志,查不出问题,打日志影响性能。所以有一些系统会有日志染色/加白机制,再加上请求重放功能,基本能解决一些 corner case 导致的异常问题的排查

形象化一点,一个请求经过服务是一条弯曲的路。日志就是里面的标识

模块化

模块化就像文章的段落。我们都知道,文章的段落,段内要统一的去表达这段的中心思想,然后,段间就是少量转折。这就和代码说的高内聚、低耦合是契合的。

模块化有几个好处

  • 方便读。脑袋能一次处理的东西是有限的,所有东西搅合在一起,就根本没办法理解。模块化后,我只要关心当前一小块的内容,不需要去关心所有其他代码

  • 方便修改。把相关的功能限定在一个比较小的范围内,功能的修改就不会到处改动。真的不要小看这个点,所有改过的代码都是要有风险的,少改代码就是减少没必要的风险

什么是模块化 ?它其实类似于收拾家里的东西的时候,做个分类。比如碗都放碗柜里、衣服都放衣柜里、玩具都放玩具篓子里。如果发现没有合适的容器,就买一个(相当于新建一个文件夹)。写代码就是打点的代码放一起、访问外部依赖的代码放一起 ...。这里真正难的是大项目怎么捋顺。

还有一个点,模块之间不要循环依赖。开始设计模块的时候,就需要分层,模块一般是树状结构,不是线性的。模块设计好之后,应该写进 README 里面,标注好每个模块的层次,不能胡乱引用。比如,常见的 utils 它就很容易去引用业务代码,业务代码再引用 utils,这是非常不对的。utils 它应该是与业务代码无关的代码

但是,我最近用 AI 写代码,发现它会容易胡乱去分文件夹。大部分程序员写代码是层次过少。而 AI 一旦发现循环引用就开始分文件夹,这也是不对的。要先考虑是否是逻辑上有问题,能不能在已有的结构上解决。

错误码

最容易忽视的、最容易凌乱的地方。分几种情况

  • 没有错误码,只有错误内容。如果是人读还好,可是如果是写代码判断,就非常不友好。万一某一天,觉得这个错误内容表达的意思不太准确,那一修改就是灾难。不是很常见,但见过

  • 错误码定义不清晰。拿到一个错误码以后,感觉什么信息都没有。完全不能辅助排查问题

    • 能枚举:给出错误码的列表,清楚列出每种错误的含义。适合错误类型比较有限的场景
    • 能区分:最典型的就是 http status code,用数字开头区分不同的错误类型。如果系统有多个模块,最好是模块间有区分,但是同一个错误返回提示都一样。这样看到错误码能快速定位到出问题的模块,又不至于说同一个错误提示五花八门
  • 返回给上游的错误类型。比如在 kitex 服务里,业务逻辑错误应该在 base 中返回,而不应该返回一个 err,err 应该是网络错误。

协议

最好是有明确的协议文件。thrift protobuf json 感觉都挺好的,有就行。不想每次去代码里看请求参数。

如果有描述、示例,那就更棒了

插件

业务代码一般可能会比较少用到插件。插件一般适合在有固定节点的地方定制逻辑。什么是固定节点,比如 ES 里面切词的时候,切之前、怎么切、切之后,这样的固定节点里。其实,洋葱模型(middleware)也是有固定节点的,请求前、请求后,所以 gateway 也经常会做成插件式的。

插件的加载/注册方式(动态、静态)、固定节点的设计、稳定性(最好就是插件加载/运行崩溃,不要影响到主逻辑),都是设计的重点。这里内容比较多,不单独展开了

测试

以前测试可能更多是 qa 的任务。但是,随着 AI coding 的不断发展,写测试的重要性会增加。这就和社区开源代码是一个道理,你没办法说每个 PR 都去看一遍,发现有破坏当前逻辑的就打回,那样成本太高了。怎么降低这个成本,就是让 AI 改完以后,先跑一遍单测,看是否有破坏非预期逻辑。可以认为是 AI coding 时代的保命神器,不然你可能都不能发现 AI 给你代码改坏了。

之前有 AI 生成代码没完全按照协议,GET 请求写成 POST 请求的。查了很久。

监控&告警

除了服务本身是否 panic、是否拉起失败(kitex 还提供错误日志变多告警)外。服务的监控主要就是进口、出口的请求量、耗时。业务逻辑本身需要的监控

告警一般是基于监控的。还可以有直接发飞书消息的。告警是监控服务是否发生需要人为干预异常,毕竟我们也不可能每天去看一遍日志。比如说,请求下游突然失败多了、突然走到一个非预期的分支上 ...。

告警和错误码是有关系的。一般告警会过滤掉上游请求错误的错误码,只告警服务本身的异常。如果错误码设计得不好,就有可能很难/挑不出来,需要告警的错误码

写在最后

我们在真正写代码的时候,很难每次都仔细去考虑每一个点,那样确实是太耗时了。有时候会因为一个问题纠结很久,甚至最后都没有答案。可以把它记在心里,下次去看别人代码的时候,就能看到,哦 .... 原来我上次那个解决不好的问题,它能这么解决。

这真的是一条很长的路 ....

关注公众号"字节跳动数据库",获取更多技术干货!

相关推荐
朱大喜1 小时前
机器学习驱动的异常检测:从统计基线到根因定位的工程化实战
人工智能
xcLeigh1 小时前
数学之美:数字革命背后的底层逻辑
人工智能·数学·ai·数学原理·书籍·数学之美·绝对边界
星轨zb1 小时前
[Corner项目实战]Spring Boot + LangChain4j Tool Calling实战:让AI自动选择推荐策略
人工智能·spring boot·后端·langchain4j
Deepoch1 小时前
VLA多模态架构赋能无人机 拓展全域智能巡检应用
人工智能·机器人·无人机·具身模型·deepoc
羊羊小栈1 小时前
基于GraphRAG的医疗健康知识诊断系统(Neo4j_大语言模型)
人工智能·语言模型·毕业设计·知识图谱·创业创新·neo4j·大作业
Python私教1 小时前
002 Pandas 的流行原因
人工智能·后端·机器学习
雷工笔记1 小时前
MES系列51-人防门行业 MES 质检分类体系
人工智能·分类·数据挖掘
宸津-代码粉碎机1 小时前
Spring AI 企业级实战|智能记忆摘要+自动遗忘机制落地,彻底解决上下文爆炸与Token冗余
java·大数据·人工智能·后端·python·spring·云计算
「、皓子~1 小时前
海狸IM 2.0 开放能力说明:OAuth2 接入与群推送机器人
人工智能·架构·electron·机器人·开源·交友·im