毕业进入现在的公司已近一年,完整参与了部门新项目两期的开发上线过程,作为一名后端开发,觉得最痛苦的是上线前和上线后的改 bug 阶段,面对各种突如其来、莫名其妙的bug,头昏脑涨、手忙脚乱、越改越懵,经常导致实验式改 bug、改一个 bug 又出现俩 bug 的之类的惨剧,我就忍不住想,为什么每次上线前都会有这么多bug呢?
前几天还读到一篇豆瓣文章,没有总结的就不是经验,只是经历。程序员也不能业务来了就写代码,有bug了就改bug,这样技术很难提升,也就难怪每次都会有那么多bug了。有的开发人员工作多年,接口还是时不时500,前期忙着写代码,后期忙着改bug,心累。
我从一期熟悉业务、框架,写一写边缘接口,到二期负责一个小模块,尝试数据库、程序的设计,中间磕磕绊绊,昨天也顺利上线,模模糊糊也感觉到了一些经验,于是努力总结下,虽然简单,也许能给自己和读者一些启发。(本文只针对初级水平,简单的bug,不涉及高并发、海量数据等复杂问题)
辛辛苦苦写的接口,自己测的时候好好的,怎么别人一调就出错了呢??(此处应有表情包,请自行脑补)当然可能是运行环境的问题,不过程序统一部署在服务器上,这一般是架构师或者运维负责的工作,至于编程语言或者操作系统的问题,也通通不在今天考虑范围内,今天,我们只考虑自己写出的bug。
事实上,运行中的程序所涉及的,无非三样:资源(cpu、内存等)+ 算法(程序运行流程)+ 数据(用户输入、数据库、第三方接口等)。通常我们认为资源是可靠的,出现bug主要是由于算法的不可靠或者数据的异常。
更进一步,机器严格按照0/1执行指令,算法上一次执行正常,为什么这一次会失败?本质上还是因为数据变了,而算法没能覆盖此情况,因此,要想保证接口的稳定,主要从两方面考虑:保证数据的可靠性、算法的健壮性,而算法的健壮性也就是考虑到数据的各种情况,两者密不可分。
如前分析,数据的变化是接口bug最常见、本质的原因。而其中,用户输入又是数据变化最主要的原因。而程序必然要有用户输入,否则毫无意义。
编程界有句名言:永远不要相信用户输入。你永远不知道,用户会在一个期待姓名的输入框里都输入些什么。不要因为前端做了过滤你就放心,一方面是用户可能会使用爬虫等手段直接访问你的接口,另一方面,前端也是你的用户,沟通也存在误差,前端可能会使用错误的方式调用你的接口,而这种错误可能会更加隐蔽。
第一条建议:严格校验用户的输入,包括格式、内容。
我知道很多人都懒得去逐条检验用户输入,觉得只要功能正常就ok了,但是,这经常会导致后期改bug时投入更多的经历。经常测试提了bug,你查来查去,发现是前端传错了参数,或者没有合理限制用户输入,当然你可以很刚,让前端去改,但这个过程已经浪费了你大量的时间精力,还不如一开始自己做好检验,返回合适的错误消息,会为你后期节省大量的精力。
对于PHP等动态语言,尤其如此,例如我们使用Laravel框架,我会在所有接口入口处,首先使用$request->validate()检验所有输入数据的格式,如有必要,还会写代码进一步校验输入内容,比如时间范围、请求数据是否有效等等。
第二条建议:考虑用户的骚操作,重复提交、延时提交
重复提交应该是大多数后端都能想到的情况,也就是接口的幂等性,有些资源只能操作一次,必须进行校验,其实不仅是重复提交,还包括同一事件被两人重复处理的情况。
而对于延时提交,其实是测试给我提bug后我才意识到的问题模式。例如我们通过get接口返回给用户某种资源,用户可以通过post接口回传资源id并提交修改,由于是自己的get接口返回的,我们可能想着只验证id合法就行了,看似形成严格闭环,但如果用户停留在此页面延时提交,则可能在此期间资源过期,或者资源已被他人修改,而改用户也成功修改的bug。其实进一步思考,你会发现,这跟高并发情景下的资源失效有异曲同工之处。
第三条建议:检验数据库、第三方接口的返回数据
除了用户输入,常见的数据来源还有数据库、第三方接口。相对而言,这些数据接口会可靠的多,而且内容格式也更规范。不过为了接口的稳定性,最好也做一些检验。如常见的数据为空的情况,就要及时中止程序执行并抛出合适的信息。
对了,对于数据库,我还遇到过bug,就是主从延迟导致的数据更新问题,由于经验尚浅,这类问题不很擅长,就不再写。
第四条建议:程序算法尽可能覆盖异常情况
这条实际上是对前三条的补充,有些不合法的用户输入你可以直接中止程序并返回错误信息,但有些情况可能需要程序继续运行,进行特殊处理,这些情况你在程序设计之初应该尽量考虑周全,后期bug会少很多,也更容易维护。
最后,再写一点点关于关于接口效率、代码质量的思考。
1. 影响接口效率的主要是数据库操作
以我有限的经历来看,接口耗时长基本都是因为数据库操作不合理,我们大多数的业务代码并不会有性能问题。我见过不少在for循环里查询数据库的代码,一定要避免,我们可以先一次性取出所有数据,然后逐个去处理。例如我们会在框架层记录所有数据库操作,调试接口时即可看到所有数据库操作以及相应耗时,该合并的查询要合并,该优化的耗时查询相应去优化。
2. 合理使用Exception,日志
这条主要针对php语言,由于历史原因,我看到不少代码靠return中止程序并传递错误信息,这样在代码复杂、调用层次深了以后极难维护,远没有Exception机制直观方便。还有,重要信息一定要写日志,便于后期发现问题及调试,也可用来自证清白。
3. 代码要合理划分、抽象
不要复制粘贴代码,重复的功能要独立出来;设计时要合理考虑需求变更、扩展;写小而专注的函数,不要把复杂功能一坨实现;这样写的代码才易于修改、测试以及扩展。这块我做的也不好,上线后看自己的代码都是一坨一坨,难以维护,接下来还要多思考,多实践。
祝大家写的代码都没有bug!
更多Laravel相关技术文章,请访问Laravel教程栏目进行学习!
Atas ialah kandungan terperinci 后端开发:如何写出可靠的接口. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!