最近我一直在谈论文档,虽然写了一篇也分享出来了。但是说实话非要自己写的时候其实大家都不愿意把这个东西落到纸面上,因为可能过于纸上谈兵了,所以我觉得可能不仅是需要一篇例子,更需要一些技巧和方法论。所以我们简单讨论一下。
科学的分析方案文档的利弊
猫会喵喵,狗会汪汪,鸡会什么?
机会留给有准备的人。
先说点虚(?)的,等我们的需求或者项目出名了、别人需要学习需要你给出一波装逼Time的时候,丢出一个图文并茂的设计文档。肯定比丢一个代码仓库获得的敬仰多吧?
或者说点实在的等回头职级评审的时候,一个复杂的需求你都想不起来怎么设计的了还得现画流程图贴代码贴效果......(嘘!)
掌控力
先说说对个人成长的意义,
往日我们提到前端最大的优势 -- 所见即所得。
要说这确实上手门槛低,比起客户端的编译动辄一根烟(就目前来说咱们客户端需要一个小时编译,一包烟都抽完了)。服务端部署还要上云要滚动来看,前端的试错成本实在是太低了,这是一个巨大的优势,特别是在试水的时候。如果用一个模块的开发比作一道考试题,我观察过一些同学(当然包括我自己)都是写几行切一下屏幕看看情况,但那就好比推导写完第一行就去对标准答案一样。获得的是很碎片化的思路,长期这样少了一些 “服务端式” 的锤炼,对较大的模块或是复杂场景就很难在脑海中形成一个比较清晰的响应。
技术方案可以在一定程度上起到强制大家先想好再写的作用,补上这部分练习。时间久了貌似就能发现自己能在脑子里推导整个代码的运行状态了,这样bug率就会自然而然的低下来,也更容易做出一些前瞻性的设计。
当然我最初的想法其实只是留下一份好得设计文档可以供以后维护代码或者需求或者方案的人能够知道最初的开发者到底是怎么思考的,如何设计的。以及有哪些隐性的问题可以从文档中找到答案,让开发人员的心智负担更低。但近一段时间的思考和询问让我觉得上面这些问题也有必要说出来。但是否需要先写文档再继续开发这点我觉得到要看具体的落地场景。
试错
俗话说,船小好调头。
我相信大家一定都有这样的经历:比如我们需要写一个精巧的多模块重业务的组件。
一开始模块划分非常合理,状态传递井井有条,然后QA提了一个BUG,在X条件下X元素的位置没有对齐,于是需要补一个透传链路传到某一个sub组件里,或是Sub组件需要一路开回调开到顶层之类的。或是你设计了一个完美的组件,然后PM说,我需要在X条件下加一个X元素位置在X坐标,并在后续的一个月时间内增加了Y、Z、α、δ.......
当然这还算好得,更坑爹一点的是,可能开发末期发现, 诶??我怎么特么不把这几个组件抽成一层让他来负责 XXX 然后集中在 XXX 里 XXX 呢?------ 算了来不及了等下次重构吧 、 下个需求在重构吧 、这特么这么多东西重构成本太高PM不会同意的 就这样吧。
有了更好的思路,却因为时间和工期卡点的原因无法诉诸实现,只能被迫补丁上线的感觉,相信我这并不好受。打开beetle看看上线分支,序号都快四位数了?
而方案就能很好的避免这一切,比如在方案进行的过程中发现了这个问题,可能还没到模块设计阶段,后面直接就按更好的方向去设计了,或者只是改几个图就行。因为还没进入Coding阶段,进行大块的调整,一般都没什么风险,就算真有,如果在项目前期提出 叫“风险”,在项目末期提出那就变成“事故”了
当然文档不是万能的,我相信这世上不存在一劳永逸的事情。但我相信这能避免大多数此类问题,能减少多少会随着代码掌控力的提升同步提升。具体怎么做会在下述中展开。
回忆
沟通成本与记忆成本
随着业务复杂度的提升,能够单打独斗的项目会越来越少,更多的项目会要求大家合作进行(后端合作也是合作-不是),也有很多时候会随着时间推移加入进来共同开发,此时有个文档给新同学介绍,起码能节约本人70%的口舌吧。(特别是我们作为开发人员口舌也不是特别好得情况下,应该也没人愿意一整天给新同学讲解吧。)
在刚开始的时候,凡事儿写文档里再说确实会在短期内提升沟通成本,但是随着开发进度过半,大家的记忆力开始逐渐衰退的时候,开头写的方案可以起到的唤醒记忆的作用就会逐渐凸显。
代码之外的额外信息(我认为目前最重要的部分)
从代码中,大部分时候我们只能看到他做了什么事情,但是“不做的事情” 其实也是很重要的信息。比如有时候看代码你可能会疑惑,这里怎么不做个缓存呢?这里怎么不加个回调呢?这里为啥......
不做的事情无法通过代码本身来传达,但是如果在方案中有讨论,并记录。就可以很好的解答这部分的疑惑。并且也可以在方案中宣讲自己设计的动机,不仅讲 what 还讲 how,加深参与同学的理解,降低这个同学的心智负担。就可以避免你设计的精美的模块被来支援的同学随便加了个跨组件的回调,再被后来入职的萌新同学滥用导致最后变成屎山。
When i wrote this, only God and I understood what I was doing.
Now, God only knows
套路化的文档解决方案(当然这是我的套路 - 一家之言)
其实这个也是一篇文档,也能看出我的性格和风格是比轻松而无拘无束的,(你以为我是瞎写?其实我就是瞎写的。瞎写又不用有负担.jpg)
方案文档通常要包含一些必要信息,只要信息到位了,章节标题我认为可以随意发挥。
简单来说就是:
我要干什么
我要怎么干
我为什么要这样干(我为什么不那样干?)
当然还有我比较关注的:你是不是有啥坑需要标注一下。
那我们就娓娓道来,这几部分应该如何展开。
我要干什么?
这一阶段我想把他称之为 需求分析, 随着我们的level提升,对语焉不详的需求的承接能力需要一个提升。
明确产品的需求
萌新阶段,产品:“抄这个”,这种需求要我我肯定怼回去了。(其实我没胆子怼,都是钰哥怼的)。但是很多时候对于一些复杂场景并不是产品自己可以hold住的,也会提前接触部分技术同学进行预研,我们需要把产品不明确的需求转变为明确的需求,进而转变为技术描述的需求。
在小风还没走的时候,他经常问我一些问题,大部分都是这种情况。当然我感觉我已经锻炼的还可以了。
比如“抄这个” 这种需求,我们就可以进一步追问来获取更多细节(其实想想我做的也不好,我大部分说的都是 都可以做排期就行了,但其实我没概念。),到比如“你是不是想抄他的这个布局机制,为了这个机制有这样的利这样这样的弊,我们可以这样这样,你看怎么样”,这时候我们的段位就高起来了。也能更好的让我们有更多机会去接触产品设计,让技术更多的融入产品。这一过程是一方面是协助产品明确自己想要的东西到底是什么,另一方面也是帮你自己减少后续风险点。
拆解为技术点
明确了产品的诉求之后,我们就需要再把产品的需求转变为可以被技术描述的需求。并且先给定优先级,有些需要取舍的场景可能会需要这个。
拿之前奢侈品的筛选需求来举例子,大概就能拆成这么几块。
产品想要一个通用的筛选组件(基础能力)
产品想要在这个通用的能力上大有作为做一大堆事情,期望能把配置化做好,能提供解决产品各种各样稀奇古怪的要求的能力。(明确要做到的技术点)
我要怎么干,我为什么要这么干?(上)
一般写方案文档有两种情形
我基本已经有了较为明确的思路,我需要论证他的可行性。
我是谁,我在哪,我怎么就tm接了这个活?这玩意儿咋做?毫无思路!
对于第一种,我认为,我思考这个问题的时候已经有了大致思路,接下来就是在纸上进行推演,避免落地后翻车。这种暂时先不展开,后边会讨论到。
那第二种对于一个无从下手的方案,我该如何展开呢?
我认为该有这么一节:【方案思路】
怎么说呢,写方案之前肯定也没啥思路,但是随着这一节的讨论,我的思路会逐渐清晰,我完全明白应该怎么做了!(其实我也没经历过这一步,后续的技术需求可以这么搞。)
梳理核心链路
大多数调研类需求都会涉及一个“如何打通XX”的问题,这样的需求大多可以归纳成为一条或多条核心链路,然后围绕着如何确保核心链路顺利运转来展开。所以对于这类需求,第一件事:先画流程图。
以我之前做过的停留时长来举例 - 需求是停留时长,停留时长是怎么被计算的呢?这一套链路是什么。如果需要多端适配,那么每个端的这套链路如何被打通呢?
组件设计类方案则不一定会有如此显著的流程图,可能更注重模块划分,比如比较复杂的页面有20个组件,他们之间说是相互独立但又有微妙的联系,不过即便如此,也并不是没有核心链路, 可能是需要提炼。(这一点在和浪总多次的希望zzui能够提供一些组件时能很好的体现,我们并不能给浪总说我这个需求是啥,而是需要提炼一下,我需要zzui提供一个什么样的组件,他需要有什么样的能力,和一个什么样的API让我使用。)
这里可能也有一些经验层面的判断在里面,如果大家要是拿捏不准我觉得可以抛出来大家讨论一下。
举个例子吧
比如筛选组件,核心在于 - 多组件之间的联动和数据解析。
比如停留时长,核心在于 - 计算之后的数据应该在什么时间节点发送且数据如何保存。当然这还伴随着多端兼容的问题
筛选组件的核心链路
细化流程
确定了核心链路之后,就需要进一步细化了。怎么细化呢?- 靠扪心自问。
我和一位大佬聊天的时候听他讲过信息论的一些东西,最让我印象深刻的叫做:“信息是不确定性的减少”。什么意思呢?比如我现在有一个比较复杂的需求,还需要server的同学提供一些能力。我需要盘一下手头的信息,然后整理一波,问问自己要做这个事情还差什么。(到目前为止主要差时间和想法)
比如前期:
是不是要用到一些奇奇怪怪的知识点?比如webRTC、webSocket之类的,我不会?怎么办?,那方案里标记一下我不会(然后学)
比如用到一个第三方的库或者解决方案,怎么用?不知道。得去看一下。
怎么对接服务端?不知道,需要问服务端的兄弟或者让他们给出文档。
然后上面第一轮问题问了一遍,继续问自己第二轮
我和server端的链接是否是稳固的?断线了断网了咋整?
服务端和RTC稳固吗??
类似这种问题,当然第二点貌似看起来不需要我太去关心,但是我认为还是问一下比较稳妥。万一这个问题去问服务端的同学说不定他们没想到但是被你提醒以后风险-1
那还可以换个方向问
前端会出现什么意外需要处理吗?
server端会出现什么意外,导致我需要处理吗?
这些疑问又会让我们催生出更加完整且有强度的设计。
还有一些常规问题。
这套方案的api该怎么设计。我需要让别人怎么用,接入成本小一些?
如果方案过于复杂是不是这套方案就行不通?是不是该放弃这个想法?
类似, 总之就是不断给自己的方案挑刺的过程,用一轮一轮的badcase来恶心自己,看看自己的方案能否扛住。
就我之前停留时长的经历来说,我对于走一步看一步的实现感到非常的畏惧。一遍一遍的写出来却遇到了各种各样的问题非常折磨人,而且代码一遍一遍的改,到后边根本就不在意代码怎么组织了,只想快点完成这个任务。
经过一轮又一轮的“扪心自问”,我们会发现一系列的不确定性的东西正在渐渐变少,以往这些没想明白的点在项目中后期会表现为风险,这个项目可能就会相对稳的多。我倾向于用问答的方式来维护这些问题, 确保是能得到解答的。当然我这个是在最后QA环节。当然也不是所有问题都能找到答案的,解决不了的,那就只能找leader或者抛出来一起讨论了。
最终,所有的问题都得到了解答,或者有了临时方案顶替不至于捅娄子。那这个方案就八九不离十了。接下来我认为就进入到下一个阶段,模块设计了。
我要怎么干,我为什么这么干?(下)
同样的标题,用了两次,是因为这两部分很多时候是混在一起的起码是挂钩的,所以大部分时候他俩都是写在一起的。至少我貌似就是写在一起的。
比如,我可以方案思路之后直接形成完备的模块划分方案。
那模块划分到底是什么呢?前半截的思路部分,其实是挖掘出需求的全貌,降低不确定性,提升技术性。而这里的模块划分,则更偏向于代码组织,确保设计的良好,可行的落地方式。
举例来说,思路一节里发现我们要做的事情有 A,B, CDEFGH。这要这些事全做了,基本这个大需求就稳了。但是落地上肯定不能函数套函数塞进一个文件完事对吧。那其实我们需要一个逻辑层面的划分。
我们把每个即将分配的模块都视为一个个的工人, 我们接下来就需要把之前的ABCDEDF分配给这些人。那怎么分呢?这个就很艺术了,基于每个项目的具体情况都有极大地侧重点的不同。这个就只能具体项目具体分析了。
举个例子吧,对于奢侈品来说大部分的能力是通用的,所以奢侈品的组件需要尽可能的通用。但游戏的需求可能更多更灵活,更流程他就更需要更灵活的组件,等等。而且我们应该保证一些指标,比如现在在提的加载速度优化。(我之前设计筛选的时候就没有顾及到这个)
模块划分需要有多细呢?
其实把主要的一些大块拆出来,能维护就行了。咱们的需求也没有大到那么离谱,还记得我前几个月说的compositionAPI吗?hooks拆起来啊 同学们!
纸上谈兵到运筹帷幄
其实我说了这么多也在纸上谈兵,对于我之前踩到的坑来说,我觉得我可以提供一些想法。
快速展开
首先,写方案并不是线性的。并不是思路完全好了以后在去写方案,因为前面也提到了试错,其实在方案上掉头的成本还是挺低的。先列出来一堆问题,在去一个个解决把他们都记下来其实方案就写好一半了。
高效传达
图片 >> 文字
我们都知道视觉的传达比文字更容易理解,所以引入更多的图,引入更少的直接代码,都可以很好的帮别人理解你的方案,而良好的图也可以更好的组织自己的语言。
这也是为什么我认为用飞书写文档会好一些,因为dashen画图的体验实在是太差了。差到极致了!
这里其实还有个题外话,以前我画图感觉很有包袱,比如流程图必须圆角矩形起止,菱形判断节点,(都是大学论文害的。)但是我的一个好朋友帮我破除了这个包袱,whatever,我觉得重点应该是抓住核心:传达信息, 只要把想传达的信息传达到位了,图其实丑一点都无所谓。而且不用严丝合缝的画图其实真的挺快的。我上一篇例子文档的3张图我原本以为要画几十分钟,结果10分钟3张都画完了。如果不是什么正式场合,这种非标准的图我觉得大家都可以随便画画,意思到位就行了。不需要有那么多负担。
颜色的使用(构想)
画图的时候如果有什么侧重点,可以用颜色的变化来解决这个问题。(当然我觉得这个可以当做职级评审时的一种手段??)我觉得效果还是挺明显的,不过这里有一点,颜色应该相对少。因为 都突出 = 都不突出了
回收验证
在方案完毕,开始进行开发以后,也不是把方案就丢一边了。我觉得应该在适当的时候打开方案再看看当时是怎么想的。
一开始我说了对答案的故事?在开发中也能验证自己的方案设计水平。
设计模块是否按预期落地了?差别有多大?为什么?
选择性写方案/重复利用
并不是每个需求都要写方案,一些小需求排期在1、2天我觉得就没有必要写。方案应该对应的是一些自己不能拿到需求就清楚那些东西该划分成什么样模块需要怎么写的时候才需要写文档。
当PM反复在对一次设计的文档中的内容反复进行迭代时,我们应该基于该文档去维护,而不是重写。这样也能在重复整理的过程中更加清晰的认识这套需求是如何运作的,且方案在重复修改的过程中,也能知道未来对于该项目如何进行重构。(找到优化点进行优化、是否能更加自动化......)
总结
-- 设计阶段 --
明确产品需求 背景和为什么写清楚。
为了完成这个需求我们具体要做哪些事情。
梳理核心链路,这一套实现的核心链路流程是怎么样的。
把链路的各个节点细化一下,具体我们需要如何实现这个节点。
-- 项目组织阶段 --
把实现各个节点的抽象逻辑细化到模块图。我们如何组织这个节点。
方案并不是一蹴而就的,他可能是伴随着你的开发而编写的。
开发完以后回头拿方案验证一下落地效果如何,和方案有什么偏差,在去把方案修正一下这就是一份存根文档。
当这个需求有迭代或者功能追增的时候,这个文档就是你下一篇方案的起点。
PS:过程中遇到各种各样的意外问题,或者一些不寻常的坑记得备注。不仅要写how 还要写why
什么样的需求需要文档呢?
举个例子:
这个需求需要长期维护
这个东西他有一定的复杂度,他不容易直接从代码中能窥见整个需求的全貌。
这个东西需要提供给第三方使用。
这个东西需要各端协作,有依赖除业务侧技术外的其他关联,如中台 搜索等。
就这么多~ 大家有问题可以讨论下。我在这里只是提供了我的一些思路。文档这个事情我不能要求大家去写,我提出了一些问题,也提出了这个解决办法。希望大家在遇到上述问题时能用方案文档的解决方案来解决。呕心沥血之作!希望大家轻喷~~