来自谷歌的开发心得:所有SQL和代码,都没必要藏着掖着
谷歌在代码管理上很有特色,他们基于“主干”进行开发,并且将 90% 以上的代码放在名叫 Piper 的单一代码仓库中,由来自世界各国数十个办事处的数万名软件开发人员共享。对于那些开源的、需要外部协作的项目,代码才放在版本管理软件 Git 里,主要是 Android 项目和 Chrome 项目。
整个仓库采用树状结构,每个团队有自己的目录,目录路径就是代码的命名空间。每个目录都有负责人(owner),负责批准该目录的文件变动。
这种方法已经在谷歌运行了 20 年以上。2015 年,谷歌的这个代码库就包含了大约十亿个文件,并且具有约 3500 万次提交的历史。代码一般提交到主干的头部,保证所有用户看到的都是同一份代码的最新版本,支持文件级别的权限控制,99% 的代码对所有用户可见。只有少部分重要的配置文件和机密的关键业务,设有访问限制。所有的读写都有日志,管理员能够查到谁读过这个文件。
与给代码制定各种访问权限的管理方式相比,谷歌的方法带来的好处是很明显的:任何人都可以浏览和使用全公司的代码,大大促进了代码的共享和复用;具有统一的版本和路径,不存在找不到文件的最新版本这样的问题;每次代码变动,很容易撤销或者用预提交测试它所造成的影响......
就连 SQL 的管理,谷歌也在一定程度上遵循了这些原则。本文作者是一名数据工程师,给谷歌当了两年的供应商,在期间发现谷歌的数据工程师对待 SQL 的态度,跟软件工程师们对待代码的态度非常相似。他认为这种态度非常重要,无论大小企业都值得在数据战略中采取这样的心态。我们将作者的文章翻译了出来,通过他的文章,我们将一同了解谷歌这种把 SQL 当成代码的态度有哪些助益,又能给体量较小的组织机构带来哪些启示。
跟面向对象代码一样,SQL 编写起来也相当耗时、调试难度不低、内容难以理解(不利于版本控制),而且高度强调可维护性。单从谷歌数据工程部门自身的情况来讲,SQL 完全可以用来创建数据管道,而数据管道又特别强调易调试、易修复。考虑到这些因素,代码集中度越高、数据战略的落地实践才能越顺畅。
所以把 SQL 看作代码,意味着我们可以把代码管理工具引入流程,轻松了解由谁负责特定变更或者维护 SQL 脚本、并持续跟踪同一作者在其他相关查询中的调整。如此一来,我们就能快速找到失败提交,恢复变更内容或者应用必要修复。在提交 SQL 代码之后,这部分代码可以被立即部署到开发环境当中。接下来就是运行开发管道,即时识别并修复故障。
另外,我们还会定期发布测试环境,通过升级保证代码能在生产版本上跑通。在测试成功之后,新旧两个生产版本之间提交的 SQL 代码才能正式启用,把出问题的几率控制在最低水平。
审视自己的软件工程文化,把谷歌这种更为成熟的文化理念设为标杆,体会并试用他们选择的工具,包括 Git、IDE 等。我们应该把所有代码都明确列入索引位置,花点时间将专用脚本转化成全局脚本,消除视图、物化视图、存储过程等一切不必要的元素。
谷歌把几乎所有代码都放在统一的集中代码存储库里。所以在需要对 SQL 做出变更时,或者需要创建新的脚本时,谷歌的工程师就建立一份相应的变更清单——在本质上类似于 PR。之后,变更流程需要接受测试并由其他工程师负责核准。核准顺利通过,作者才可以把代码变更提交至代码存储库。
虽然这种变更控制形式在企业中相当常见,但谷歌的一大特色在于高度强调代码格式的重要性。我本人以往对代码格式不太重视,但切身经历让我意识到高质量的代码格式确实能大大降低理解和调试难度、也有助于缩短其他作者在代码修改上耗费的时间。谷歌特别重视代码格式,甚至设立了一套自动化机制、直接拒收不符合编码标准的代码。
选择一套代码存储库,然后坚持以它为中心开展后续工作。在理想情况下,这套存储库应该在各工程团队间共享,至少也得保证把所有 SQL 代码集中进来。其中应该涵盖全部数据功能,包括数据工程、分析、商务智能等等。
另外,一定要推行代码格式标准化。目前市面上有不少开源代码格式标准化工具,它们使用难度不高,而且能极大提升代码的可读性与可维护性。花点时间,使用现有格式化工具或者内部的原研工具把原有代码梳理一遍,同时保证后续提交的一切代码都必须符合格式标准。几天、最多几个礼拜,数据工程师们就能适应这套新的标准,并让公司内的 SQL 代码在可读性、编写质量和可理解性方面都上一个台阶。
代码变更无处不在,如果没有版本控制加以约束,我们将很难用回滚来补救意外错误。万一提交的代码破坏了管道、产生了预期之外的值或者跑不通,那我们就得利用版本控制恢复至上一个正常状态。
谷歌的代码集成原则也遵循这样的思路。哪怕变更内容再离谱(真的,有时候提交的代码就是这么离谱),拥有良好结构的测试环境也能从容消化,不致对正常业务造成干扰。如此一来,SQL 变更对于开发环境的影响就能直接体现,帮助工程师们快速发现故障。
当然,也有极少数代码在开发环境中不会立即造成故障,但在生产环境里却问题频发。造成这种状况的因素多种多样,所以我们需要在测试体系中引入单独的预生产环境。
谷歌使用环境变量来管理多种测试环境,这些变量可以通过解释层被轻松注入至表名当中。
至少要建立一套开发环境,同时尽可能扩大代码测试所涉及的数据基础设施范围,这样才能把出现故障的几率降至最低。DBT 等免费开源工具就通过抽象层显著降低了测试难度,其中所有表都有两个版本,一个是开发版、一个是生产版。这样在每日、每周乃至每月的发布计划中,我们就能安心把上次发布计划之后提交的所有代码都直接提升至生产环境。
正所谓“成也萧何、败也萧何”,谷歌把几乎所有代码塞进单一代码存储库的作法,导致我们很难分清某一产品究竟归谁所有、又有哪些人在使用。例如,如果不能广泛访问这套集中代码库,软件工程师在更新生产级应用程序时就很难理解变更会引发哪些下游影响。而在获得广泛访问能力后,他们可以轻松搜索到对当前应用程序构成依赖的脚本、查询操作及其他应用程序,并通知相应工程师开展变更协同。
我知道,很多企业总想用代码保密的方式把不同开发环节隔离开来。没错,某些高度敏感的项目代码库确实不该随意开放,但这类项目不多、而且跟其他项目间的联系也不会太紧密。既然谷歌这样规模庞大的企业巨头,在建立代码架构时都愿意充分发挥信任的力量,那其他小公司真的没必要总是藏着掖着。
在代码库和存储库的结构设计中多多引入信任与沟通机制。至少从工程技术的角度看,你的项目绝没有想象中那么“见不得光”。毕竟如果你连团队中的工程师都不能信任,那这家企业还能正常运营吗?总之,请主动在业务流程中的软件工程与数据工程之间打通边界、鼓励协作。也只有这样,我们才能在代码在生产环境中落地之前,抢先一步解决由变更引发的负面下游影响。
原文链接:
https://blog.devgenius.io/why-google-treats-sql-like-code-and-you-should-too-53f97925037e
点个在看少个 bug 👇