面试官:为了写好代码,你坚持了哪些好习惯?

228 阅读8分钟

现在的技术面试,分为两个流派,一个流派是以考查候选人八股文 + 算法为主,另一个则是根据候选人所做的项目,循序渐进地往里深挖,在辅以一些没有标准答案,涉及日常工作积累的开放性问题。

其中的一个出场率很高的,比较吃经验的开放性问题就是,“为了写好代码,你坚持了哪些好习惯?”

我大体总结了一下,涉及如下这些。

1、入参充分校验

有个说法叫做All put is evil,即:一切的入参都是邪恶的。

所以,一个经验丰富的工程师,应该会对接口入参进行由浅及深的全方位校验,从简单的参数非空判断、长度判断,到完完整整的业务校验等。

举个例子:一个典型的电商下单场景,我们需要校验下单用户状态是否正常、商品是否上架、商品是否还有库存、优惠券是否可用,等等。

请注意,这个必须要养成习惯,很多低级错误都是这么导致的。

2、日志要打全

我们当然希望自己写的代码,无论是在自己的本地环境,还是在测试环境,以及最后发布到生产环境,所有的业务主逻辑和分支逻辑都是完全正确的,每个业务请求都是正常执行的。

但真实情况往往并非如此,我们在面临个别请求的业务异常处理情况,哪怕我们在微服务架构下有ELK、Skywalking这样的日志收集和链路追踪的利器,但业务日志打印得是否全面合理,仍将决定我们排查问题效率的高低。

记住,你写代码不打日志节省下来的时间,将来一定会在排查问题的时候,给你加倍找回来。

3、RPC要考虑网络

记住,RPC调用,跟injvm本地调用不一样的,因为前者是要经过网络的,而网络本身就是不稳定的。

这种情况下,你需要考虑:

(1)我发起RPC调用的时候,请求没到服务提供者,要如何处理?

(2)我发起RPC调用的时候,请求已经到了服务提供者,但没有成功返回结果,要如何处理?

(3)RPC调用的超时时间设置多少?重试次数设置多少?

对于以上的三种情况,读请求和写请求的处理方式是不一样的,边缘业务和核心业务的处理方式是不一样的,高并发和非高并发尝尽的处理方式是不一样的,一定要case by case,谨慎处理。

4、循环单次改批量

很多新手同学,在需要多次执行比较耗时的RPC或数据库操作的业务场景下,由于本身不重视或图省事等原因,没有新增一个可处理批量的RPC接口或SQL语句,而是选择了在循环中调用原有的单次执行的RPC接口或SQL语句。

这种情况,如果是个屈指可数的小循环还能接受,但赶上那种几百上千次,甚至更多的循环,性能瓶颈就马上显现出来了。

另外,这种大循环场景下,打印日志也需要特别注意,往往由于在循环体里加一行日志,导致每天的日志量剧增,特别消耗磁盘资源。

5、复杂SQL先执行一下

那种海量数据下多表关联查询的SQL语句,一次执行把整个数据库搞挂并不是完全的小概率事件。

因此,如果你写完一个这样的SQL语句,一定要记得怀有敬畏之心地先执行一下,看看效果。

记住,不要在测试环境的数据库中执行,因为有的公司的测试库和生产库的数据量是天差地别,数据分布形态也不一样,执行结果可能没有参考性。

记得要去生产库的从库去执行,因为主库一旦出现问题,影响范围太大。从库也不要直接去执行,执行之前还是要explain一下,看看执行计划的。

如果执行计划中出现了大表的type=all,那也就别再执行SQL了,把从库打挂了影响也不小,先想办法优化吧。

6、扩展或新增,别修改

经常看到这种场景,一个刚入职不久的新人,负责维护某个中台服务,在接到A业务线产品经理的一个需求后,开始大刀阔斧地对原有的业务主流程进行了修改,改完后自测没问题,QA测试也没问题,于是自信满满地上线了。

上线后没多久,B业务线的工程师慌慌忙忙地找过来了,说本次上线影响了他们的业务逻辑,要求马上回滚代码,否则业务损失会非常严重。

这里要说的是,如果你负责中台服务,并且你对业务全貌还不完全熟悉的情况下,对业务主流程的变更宁愿采用扩展或新增的方式,不要贸然直接做牵一发而动全身的修改,哪怕影响一些代码的复用性。

7、重构,不重写

有一种自以为是的工程师,以“重构”代码为荣,动不动就堆产品经理和业务团队说,“这两个月我们不接新需求了,工程代码需要重构了。”

我只想说,“大哥,你这种行为不叫重构,你这叫停业整顿,叫重写,明白不?”

我们来看一下两者的概念吧。

重构:不是对已有代码的全盘否定,而是对不合理的结构进行调整,合理的模块进行改动;利用更好的方式,写出更好,更有维护性代码。

重写:已有的代码非常复杂混乱,难以修改,重构的时间还不如重新写一个来得快;根据需求另立一个项目,完全重写。

所以,我们写代码的时候,一定要养成“时时勤拂拭,勿使惹尘埃“的重构习惯。

8、非必要,不引入

百度技术团队有一句口号,叫“简单可依赖”。

在满足于目标和需求的情况下,代码实现越简单越好,架构设计越简单越好,技术栈越简单越好。

所以,非必要,不引入。

  • 如果只用MySQL就可以抗住业务请求,那就绝对不引入Redis和ES;
  • 如果同步RPC调用可以满足业务,那就绝对不引入异步MQ;
  • 如果简单coding即可高效迭代的需求,那就绝对不引入设计模式;

只有不引入,才不会增加系统复杂性。

一个出色的工程师,他的特质是喜欢迎接挑战,但一个营收惊人的关键系统,对它最好的方式是杜绝挑战。

9、保证数据一致性

为了满足查询快,多样化查询的业务场景,这时往往数据源会从一个变成多个。

多数据源模式,最常见的就是MySQL主从模式和MySQL + Redis组合。

其他的,还有如下这些:异构数据库、数据库的明细表 + 聚合表模式、、MySQL + ES,MySQL + MongoDB,MySQL + Doris等。

这种情况下,我们首先要明确,哪个是主数据源,即:如果发生数据不一致的情况,我们要以那份数据为准,往其他数据源去进行同步。

其次,当数据源的数据不一致时,我们如何做策略,才能让多数据源的数据满足最终一致性。

否则,这将给系统留下极大的业务隐患。

10、杜绝过度前瞻性

有这么一种可爱的工程师,系统前瞻性刻在了他的骨子里。

一个人在开发一个孵化型项目的时候,叮叮咣咣拆分了十多个微服务,号称这是做好了未来三年五十人研发团队的技术规划。

然后,在开发完一个功能模块,进行代码调试的时候,在自己的idea里面一通启动Jetty,一个请求链路把自己都给绕晕了。

他不但把微服务都拆分好了,甚至把数据库主表都做好分库分表了,然后找一条数据的时候,各种算id对应的模数,甚至测试环境的维护都变成了一项艰巨的任务。

然后,他这个项目上线一个月,业务进展不理想,被砍掉了。

我要说的是,人是活在当下最好,系统也是最适合于当下最好,我们要杜绝夸夸其谈的未来性。

结语

很多同学在评论区留言或私信我,让我继续往后追加内容,他们看起来有些意犹未尽,并且觉得内容并不全面。

我承认不全面这个事实,但好的编码习惯真的成体系地进行总结,且还要循序渐进,深入浅出,雅俗共赏,生动有趣的话,写一本几百页的书都并不为过。