从架构到重构

重构之后整理了一下对架构的理解,偏重但不仅限于Web前端(以下简称“前端”)。

架构师的职责

不少人一提到架构师就觉得很高大上,什么是架构师?

从上千人跨国公司,到十数人的创业公司,架构师的职责各有不同。

人数众多的IT公司通常会分成架构(框架)组和业务组,架构(框架)组为业务组提供公用的框架和组件(服务),而业务组负责开发实现需求。

创业公司的分工通常没有那么细致,往往需要开发者通常既是架构师也是业务开发者(以下简称为“开发者”)。

开发者承担的责任是某个功能模块的实现,而架构师的主要责任是为项目选择合适地技术栈,为开发者搭建一个可持续开发的框架。

通常情况下,责任和权利是对等的,这也是架构师的收入和职位比开发者高的原因。

如果把开发团队比作军队的话,架构师不是凌驾于开发者之上的指挥官。
架构师是一支辎重部队,负责提供的军械和粮草,是全军的物资保障。
架构师是一支侦察部队,负责了解情和战场,帮助战友顺利完成作战任务。
架构师是一支执法部队,负责维持秩序,使得军纪严明。

概括来说架构师的职责就是服务、引导和规范。

服务业务

项目结构
回忆一下,你在持续开发一个项目或者接手一个老项目的时候,相关地代码文件是随手可得,还是需要翻来覆去靠直觉寻找?

以我们团队最近的重构的一个AngularJS(1.3)项目为例,重构之前的部分目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- app //源码目录
- service //service模块目录
- script //js文件目录
- directive //指令目录
- counter.js //指令
- style //sass文件目录
- template //指令模板目录
- counter.scss //指令样式
- view //视图目录
- template //模板目录
- counter.html //指令模板
- test/unit //单元测试代码目录
- service //service模块目录
- directive //指令目录
- couter.spec.js //指令

在app源码目录下,我们看到一级子目录时按照功能模块划分成不同目录,各个模块下又按照资源类型进行划分。这样的似乎既有逻辑关联,又方便了构建工具处理不同类型的文件。但随着项目增大也就会出现前面提到的问题:关联的文件位于不同深层次的目录,查找和编辑相关文件非常困难。

修改一个指令(可以理解为组件)需要跨多个、多层目录,当指令增多时这个跨度变得更大,编写测试代码的时候这个问题也同样存在,所以只能借助编辑器带有ctrl+p这样快速查找文件的功能来缓解这个问题。

所以针对这个问题进行了一次重构,下面是重构之后的部分代码:

1
2
3
4
5
6
7
8
- src //源码目录
- service //service模块目录
- directive //指令目录
- counter //指令
- counter.html //指令模板
- counter.js //指令逻辑
- counter.scss //指令样式
- counter.spec.js //指令单元测试

这样的目录结构更符合“组件化”的思维,同时开发起来也更顺手更方便。当开发一个指令时,可以在同一个文件夹下找到所需要的所有文件,指令开发完成时也可以方便的编写测试代码进行测试。

“高内聚,低耦合”虽然是对于代码的要求,其实目录结构也是适用的。

技术栈

对于整个项目而言,技术栈包括了语言和框架,对于后端来说语言可选择的不少:Java、Python、Node.js…..每种语言的常用框架不多。前端则相反,语言没得选(不考虑js的胶水语言),只有html、css、javascript,框架选择却相对较多:AngularJS、Angular4、Vue2、React、Cycle.js……

但是很多时候技术栈没有我们想象的那么重要,一方面对于很多项目的业务场景相对简单(后端多以增删改查,前端页面数量不过五十),技术栈并不会成为项目的重要制约因素。另一方面架构师、团队对框架和语言的运用能力远远大于技术本身。

一个通过拷贝和粘贴写代码的开发者肯定不如懂得组件化方式写代码的开发者效率高,一个把所有脚本都打包成一个js的大中型项目肯定比不上异步加载体验好……

架构师和团队对技术的精通程度,比技术对于项目的适合成都更重要。

API文档

如果你参加过多人协作开发的项目,一定碰到过重复造轮子的情况:开发者不清楚有哪些公用代码,或者不清楚公用代码提供了哪些接口,于是一个同样的逻辑,被写成了很多个版本。

这是一个很尴尬的问题,或许说是一个无法完全解决的问题,但还是有方法尽量去优化它。

下图是我在项目中利用编写的前端API文档,解释一下,这里的API文档不是与后端通过ajax请求交互的文档,而是前端各个模块公用代码的文档。

好的文档应该有两个要求:查阅方便、更新及时。

像这样的文档是html页面,所以既可以部署在服务器上,也可以本地查看,同时还按模块和功能进行了分类,也支持搜索,查阅起来并不困难。

这么多内容如果要纯手写的话太费劲,而且如果和代码分布在不同目录的话维护就会变得不方便,也会出现之前在目录结构中提到的问题。所以比较好的方式是采用jsdoc这种插件,利用注释生成文档。

举个例子,一个人想要锻炼身体,在离家5km一个健身房办了一张健身卡,但是没有坚持多久。虽然可能是因为意志力不够,但更有可能是因为健身房太远了。如果健身房就在小区里,下楼50米即到,不需要太多的意志力,下班的时候看到想到就很自然地顺路就走进去锻炼了。

而公共代码就是例子中的健身房,架构师要负责把它们放到触手可及、轻易可得的地方。

引导开发

开发文档

很多团队都会有一些“入职文档”、“开发规范文档”之类的。不管它们以什么形式出现,叫什么名字,它们的共同作用都是指导开发者如何写出更高质量的代码。

它的缺陷也很明显,没法保证每个人都认真看了,看了也没法保证都记得。更重要的是,文档(比如xxx开发规范)是死的,人是活的。单纯的文档没法保证每个开发者真正按照文档所述的方式写代码。

培训

培训相对于文档而言是更直接更生动的方式,但是对时间要求比较高,需要占用团队成员的整块时间,同时对架构师的表达能力也有一定要求。

但如果能坚持做起来,效果肯定比文档好(你没法保证每个开发者都看更新的文档,总可以保证开会都来参加吧)。不过作者这一块没做好,因为时间实在太紧张~

开发文档和培训对于提升团队整体实力有帮助,但是仅依靠这些往往难以达到理想效果。

所以我们需要

规范约束

lint

写得再好的开发文档都不如校验工具来得直接。

前端的校验工具还算丰富,sasslint、eslint、htmlhint就是我们团队在用的校验工具。校验工具不仅可以强制开发者遵守规范,还可以提前检查出错误。

虽然架构师无法保证每个开发者都认证地看了文档,但是如果不按照文档规范编写代码,就会造成校验工具报错、编译失败,从而无法继续开发。

若无自律,便无自由。虽然看上去似乎苛刻,但是对于项目代码的整理质量而言是有所保障的。况且一旦熟悉这些开发文档和规范,校验规则便不是问题。

git hook

如果有开发者修改或删除了校验规则(配置文件),不是可以绕过这些规范么?

这时候可以使用第二道保障措施:git hook。

建立一个hook,如果代码校验失败,则不允许推送或者合并代码,从而无法联调测试,无法部署上线。这时候开发者只有两个选择,好好按照规范编写代码还是和项目经理沟通一下这个功能不上线了……

当然hook还有很多用法,比如自动部署、发送通知等。

代码审(走)查

提到的这些都只是手段,目的还是保证项目代码质量,提高团队工作效率。

对提交的代码进行审(走)查,对不符合规范的代码及时指出,主动询问开发者是不是遇到了什么困难,把好的代码分享出来。这些都是更人性化的保障措施,只是工作量会大一些~

重构

动机

重构的动机大致可以分为两类,一类是因为当前项目逻辑不够清晰,需要进行重新整理。很常见的情况便是文件过大函数内容过多。第二类是项目不断发展变大,原有的代码组织(框架)方式已经不适合了。

无论出于哪种目的,我们都需要尽力保证重构后的代码功能不发生变化或者出现新的bug。

怎么保证?

别告诉我你用鼠标点了几下,或者写了几条假数据进去发现ok就行了。

测试

如果说校验工具是对代码风格的保障那么测试代码就是对代码逻辑的保障。甚至可以说它是进行重构的必要条件。

请举出一个不写测试代码的成功的开源框架。

你能保证你的项目代码质量比开源项目更高更稳定?
如果不能保证还有什么理由不写~
如果能保证写个测试代码又何妨~

所有的努力都不会白费。

当你坚持看文本文,开始思考、尝试文中地方法时,离心中地目标必会更进一步~

亚里士朱德 wechat
更多WEB技术分享请订阅微信公众号“WEB学习社”