拯救强迫症:井井有条的代码库,让世界变得不一样!
本文转载自公众号“读芯术”(ID:AI_Discovery)
假设你刚刚加入新的公司新的团队,要开始接触编码库相关的工作。你会面临的第一个问题就是:在哪添加团队项目的源文件呢?
问得好。新团队有三个源目录可供添加文件,你得分清哪个是你想用的,哪个是组员用的,哪里的文件是需要重构的。
Slack iOS团队多年来在编码库方面都做的较好。因为好几次想要组建一些源文件,但又缺少编码库的架构模式,再者是近些年开发人员越来越多,我们来到了这个团队。
从顶层开始
我们大约有13000个文件(还在不断增多),27个左右的顶层目录,Objective-C和Swift的混合文件,大约有40个iOS开发人员在一个monorepo中工作。
Slack iOS Xcode File Hierarchy2017
这些是所有文件层次结构状态的真实照片。新来的员工都在不断抱怨入了编码库,而我们已经习惯操控这些混乱的目录,一开始的痛苦已经不记得了。
我们有无数的源代码目录(特别是iOS,SlackCocoaSDK和Slack目录),而且确定目录要耗费好长时间,然后再决定如何添加文件。此外,我们决定给编码库增添新的工具,但是目前Xcode项目的状态不能很好地支持新增添工具的运行。
所以,我和一组ios开发人员决定开始制定以下几点规则:
- 让任何新老开发人员都能快速方便地添加新文件。
- 在目录中遵循我们的设计模式
- 借助工具能够自己维持新的且层次结构简洁的文件夹
这将分为两个步骤:先将顶层目录移动到连贯的序列中(主要目标目录、扩展目录、框架等),然后是大任务——源文件夹的组建。
顶层目录的移动不存在争议,也不难进行。可能需要几个开发员一同花费几周的时间。首次移动中,我们学到了一些后面阶段能用到的技——错过高峰期处理大的移动,始终合并master,及时浏览评论。
合并冲突不是过程中唯一棘手的事情,实际上我们可以用xcodegen更好地消除冲突,大部分冲突都存在于项目文件中。我们也想保存git历史记录,能一直一目了然地看到git和finder中的文件。但我们倾向更简单的方式,让所有人员参与进来,拖放文件到主页。
Slack iOS Xcode Hierarchy 2017(左) and 2018 (右)
从2018年9月的这张图可以看出,我们已经能够成功组建顶层目录,让每一个目录都适用且都是顶层目。
组建源文件
现在是时候处理iOS、SlackCocoaSDK和Slack中的源文件,把它们全部移动到App或Source中。
老实说,笔者很怕这一部分。我们需要有一个清晰的模式,能让团队所有的开发员都参与进来,规则和工具都要确保移动方便,工程师能清楚地看到自己是否出现失误。
图源:unsplash
笔者对层次结构的模式展开了很多调查,竟然发现关于文件夹组建的文章少之又少。Uber在这篇文章中写了他们是如何移动到monorepo中的(
https://eng.uber.com/ios-monorepo/)。这对我们如何将代码库分为小模块(规模较小)提供了启发。
最后笔者为团队提供了三个选择:
- 功能构建
- 主题(基于体系结构的组织)
- 本体(基于关系或类似分组的组织)
团队会先集中主力在高级功能,然后才是主题或功能目录中的MVVM+C。这是新结构中的一个文件夹:
/FeatureFolder /Coordinators /Models /Tests /Functional /Mocks /Unit /ViewModels /Views
移动源文件是一项冗长繁杂的工程。处理合并冲突让人冒火,搜索文件名称空间来看是否将所有功能文件都拖拽到目录中要比最初想得更难记住,而且移动的大部分文件都不符合刚开始设定的文件夹规则。
不过好在有一些勇士挺身而出,做了伟大的举措——将iOS,SlackCocoaSDK, 和Slack都移动到App/Source中。
2020年1月层次结构文件夹的截图:
Slack Xcode File Hierarchy2020
在移动这三个大型源目录时,我们提出Danger规则会阻止向这些目录中添加文件,因此会开始用我们的新模式。
Danger是持续集成系统中会用到的工具,可以执行提交后的自动检查,同时将警告和错误信息发到PRS上。这是大致模样:
has_slack_directory_additions= !git.added_files.grep(/Slack/).empty?has_slackcocoasdk_directory_additions =!git.added_files.grep(/SlackCocoaSDK/).empty?has_ios_directory_additions =!git.added_files.grep(/iOS/).empty?if has_slack_directory_additions ||has_slackcocoasdk_directory_additions || has_ios_directory_additions fail(‘This PR is introducing new files intodirectories that are closed for adding new files. Pleaseadd files to App/Source using the new convention found in<ahrefahref=”…Adding-a-file-to-Slack- iOS…”>Adding a file to SlackiOS</a>’, sticky: false)end
遵循Linter
到这里还没有结束,我们仍需移动新源目录、App/Source中的内容。这里列了“文件夹内务管理”过程中的一些规则:
文件夹名称不用留多余的空间(类似Bazel这类工具效果不是很好)
不要像“Helper”或“Uitility”那样颠倒文件顺序
共同定位测试(如果测试与源代码都位于同一个功能目录中,什么样的方式能更容易找到测试呢?)
分类文件和文件夹!(谁不爱字母排序?!)
确保文件处于文件夹中,并且文件要与合适的目标顶层目录相关联。
图源:unsplash
有一条规则是真的需要共同定位测试,所以我们选择Danger 规则。任何添加了新文件的新PR都无法加到App/Tests中。
大致如下:
has_slack_directory_additions= !git.added_files.grep(/Slack/).empty?has_slackcocoasdk_directory_additions =!git.added_files.grep(/SlackCocoaSDK/).empty?has_ios_directory_additions =!git.added_files.grep(/iOS/).empty?if has_slack_directory_additions ||has_slackcocoasdk_directory_additions || has_ios_directory_additions fail(‘This PR is introducing new files intodirectories that are closed for adding new files. Pleaseadd files to App/Source using the new convention found in<ahrefahref=”…Adding-a-file-to-Slack- iOS…”>Adding a file to SlackiOS</a>’, sticky: false)end
文件夹组创建一个Slack渠道,以供人们在不确定是否添加文件的时候进行咨询。对于往哪添加文件的困惑会比你想的多,甚至小移动都会带来很大麻烦。
我们已经有了很大的进步,得到了来自更优秀iOS团队的支持,但等待我们的是更多的事情。
这不是一个人能做好的工作,你需要跟编码库工作环节中的每一位人员协同合作起来。你需要更好的团队,并不只是为了移动文件,而是修饰规则,添加更多的工具。有更多人的参与意味着很多人会从中学习,之后便有能力教他人如何在编码库工作中添加文件。
成功和幸福的秘方很简单:假如你跟我们一样有monorepo,那就组建一个团队,制定硬性且能快速实现的规定。
规则可以被打破,你跟任何开发员都有机会讨论,你需要核心队伍来创建支持规则的工具,让文件组建更水到渠成。核心成员也可以花些时间思考寻找最佳文件结构,来服务于团队组织或工作模式。
图源:unsplash
当开发员明白在哪加文件、以什么样的方式可以加速开发、让自己在代码库的工作环境中更加自在时,世界就会变得不同。
SwiftLint、Danger、本地脚本这些工具都会助你一臂之力。但有一点需要提醒,那就是首先你需要明白工具在何处有用,这通常需要动动手指。
使用工具,共同参与,像解决其他对公司不利的问题一样处理它。这是一件超值的事情,会让大家更轻松地找到或添加文件,帮助开发员理解编码库的架构模式。