不懂产品的研发,不是好 CTO

在做产品经理之前,我是传说中第三种人类:程序媛。曾经,被产品经理虐过,吐槽过产品经理;现在,也虐过研发同学,也被研发同学吐槽过。因为做过研发也做过产品,可谓知己知彼,深刻的体会到双方沟通中的痛点,希望本文能够帮助研发同学更好的和产品经理沟(si)通(bi)。

什么是产品?

产品是指能够供给市场,被人们使用和消费,并能满足人们某种需求的任何东西,包括有形的物品、无形的服务、组织、观念或它们的组合。

比如,手机满足人们通讯需求,是有形的硬件产品;微信满足人们社交需求,是无形的软件产品;家政服务满足人们清洁需求,是无形的服务产品……

其实,简单来说,产品就是能满足人们某种需求的东西。

什么是好产品?

从以下三组图片中,选择你认为好的产品。

幻灯片02

幻灯片03

幻灯片04

第一组,相信大家都会选择小米遥控器。因为相比传统的遥控器,小米遥控器设计简洁,按键功能一目了然,不用看说明书就知道如何使用。

第二组,每个人的选择都不一样。对于普通用户来说,美图秀秀是个好产品,好用、容易上手,而 PS 完全不知道从何下手。但对于专业设计师来说,美图秀秀就是个渣渣,根本满足不了工作需求。

第三组,大家可能没怎么接触过,通过这两款产品可以实时查询公交的位置,还有几分钟到达相应的站点。仅凭一个界面,有些人会觉得车来了是好产品,因为界面干净、整洁、交互也特别好,而上海公交查询体验特别差。但事实上,我把车来了这个 App 卸载了,却一直在使用上海公交查询,因为它提供的数据非常准确。对我来说,这两个产品都算不上是好产品。

所以,好的产品必须同时具有以下三点:

  • 有用
  • 能用
  • 好用

image

人人都是产品经理?伪命题!

每次看到这句话我就特别想翻白眼,甚至想学罗胖来一句,我呸!

这是大家眼中的产品经理:

幻灯片07

你是想卖一辈子糖水,还是跟着我们改变世界

在每个产品经理内心深处都有一个改变世界的梦想。

在父母眼中,产品经理就是公司的核心,不可或缺。

在研发眼中,产品经理就是一个打杂小妹,随叫随到。

在设计眼中,产品经理就是山顶洞人,完全没有审美眼光。

逻辑不如研发,文案不如运营,审美不如设计,视野不如老板。

为什么需要产品经理?

存在即是合理。

老板表示需要一个背锅的:遇到不好用的 App,大家第一反应都是什么 SB 功能,SB 产品经理,但是从来没有人骂 SB 老板;用户吐槽产品很难用,设计可以说这锅我不背,产品经理设计的流程就是这样的;项目进度延迟,研发可以说这锅我不背,产品经理需求变更频繁……

任何人都可以甩锅,唯独产品经理不可以,任何与产品相关的锅产品经理都要背,这些都是产品经理的责任,不可推卸。

研发、设计表示需要一个打杂的:缺文案,产品经理帮你写;缺数据,产品经理帮你整理;需求开发好了,产品经理还要帮忙验收测试……

市场、用户表示需要一个翻译:例如,如何向用户解释什么是 2G、3G、4G 网络。如果直接告诉用户 2G 是数字网络、3G 是高速 IP 数据网络、4G 是全 IP 数据网络,用户肯定一脸懵逼:什么鬼?但如果告诉用户 2G 可以看文字、3G 可以看图片、4G 可以看小电影,用户立马就明白了。

怎样才能成为产品经理?

在大部分人眼里,只要会吐槽就可以做产品经理。

吐槽吐的专业,可以成为评论家;吐槽吐的幽默,可以成为段子手;吐槽吐的群情激奋,可以成为意见领袖;但发现问题仅仅只是产品经理工作中的一小部分。一个优秀的产品经理,必须会分析并解决问题。

我们来看一下产品经理的关键词:

幻灯片11

是不是有种产品经理从入门到放弃的感觉?其实,将这些关键词整理一下,可以分成以下几个维度:

1.人

幻灯片13

实现一个产品功能,产品经理需要全程跟进,在这个过程中,就免不了在多个部门之间沟通协调。

例如,老板想要增加某个功能,那么产品经理首先需要和老板沟通,了解增加该功能的原因,然后简单评估一下需求的优先级,并给出一个合理的解决方案。找设计沟通UI设计及交互逻辑;拿着做好的设计稿找到研发,确定开发计划及上线时间;功能上线后,向市场、客服部门的同事做培训,推广产品功能;产品功能推向市场后,收集用户反馈,处理用户遇到的问题;功能稳定后,采集数据,分析数据,持续运营产品;最后,还需要向老板及相关部门汇报产品数据,复盘总结。

2.能力

幻灯片14

在图中列出的产品经理相关能力中,最核心的能力是学习能力、独立思考、沟通协调能力。这里的沟通能力,并不是指性格开朗、热情大方、幽默风趣这些一般人理解的沟通能力,而是把细节说清楚,专业。把细节描述清楚,没有歧义,双方能达成共识;工作态度专业认真,不过于情绪化。而协调指的是协调所有可用资源,让事情朝着你想要的方向前进。

3.工具&方法&输出

幻灯片16

工具、方法、输出都是手段,不是目的。学以致用才是学习的目的,学了不会用等于白学。

而在实际工作中,光会这些还不够,必须把所有这些都揉碎、重组、融入到工作的每个细节中。

左边是大家看到的路径,右边是产品经理考虑的路径。

幻灯片23

左边是大家看到的需求,右边是产品经理面对的需求。

幻灯片24

左边是大家的工作流程,右边是产品经理的工作流程。

幻灯片25

以杏仁医生App药品登记需求为例,下图是用户和研发看到的。

幻灯片26

而实际上,下图是我考虑的内容。

幻灯片27

看到这里,你还认为产品经理门槛很低吗?人人都是产品经理吗?

产品经理不生产需求,只是需求的搬运工

介绍完产品经理,我们要介绍产品的另一个关键词:需求。产品经理不生产需求,只是需求的搬运工。有人可能要说反对意见,乔布斯不就生产了需求吗?其实乔布斯也没有生产需求。

需求就像煤矿一样,有的随意散落在地表,大家都能看见;可绝大部分有价值的需求就像埋在地底下的煤矿一样,需要专业的人员去挖掘,去采集,去清洗。而产品经理就是这个挖煤的矿工,优秀的产品经理能挖掘到人性最深层的需求。

按照马斯洛需求层次理论,饿了么满足了用户的生理需求,在家也能吃上全国各地的饭菜;支付宝满足了用户的安全需求,给用户提供了资金安全保障;微信满足了用户的社交需求,让用户低成本的与朋友、亲人、同事交流;知乎满足了用户的尊重需求,让用户展示自己出众的知识技能,得到大家的赞赏;得到满足了用户自我实现需求,营造一种充实感,让用户感觉自己每天都在进步……

image__1_

需求需要符合这四个条件:

  • 特定的人
  • 特定的情况下
  • 特定的问题
  • 可以被解决

概括下来就两个字:场景

之前有同事提出,杏仁医生 App 中的药品登记模块需要增加一个批量登记的功能。咋一看是不是很合理?但仔细一想,什么场景下医生会需要批量登记呢?

事实上,并没有这样的场景。医生开药的场景大部分都是先搜索药品,如果没有搜索到合适的药品,就顺手登记一下。医生并不会用小本本记下缺哪些药品,然后批量登记。因此,批量登记功能是一个伪需求。

把问题放到一个具体的场景中可以过滤掉非常多的伪需求、不合理的需求。

需求从哪里来?

1.公司外部

公司外部的需求主要来自用户、合作伙伴、竞争对手。用户经常抱怨某个功能不好用,用户反馈其实就是需求;合作伙伴在和我们合作的过程中,肯定会产生一些新的想法,这些也是需求;竞争对手推出了一些好用的功能抢走了很多用户,为了抢夺市场,也会产生需求。

2.公司内部

公司内部的需求主要来自公司战略、产品规划、内部人员。每年公司都会制定公司一整年的战略计划,产品总监会根据这些战略制定每个季度的产品规划,产品经理再将这些产品规划分解成多个产品功能。在产品迭代的过程中,老板、市场、运营会提各种各样的需求,研发、产品经理也会从技术、产品层面提出需求。

怎么定优先级?

1.刚性

指这个需求是否迫切,实现了这个需求,是不是能立马解决困扰用户很久的问题。

2.广度&频率

广度指这个需求会影响多少用户,多少核心用户。频率指用户使用这个功能的频率。

3.替代方案

指解决这个需求,是否有替代方案。

按照以上原则,规划电商模块功能。第一个版本必须要实现商品展示、商品详情、购物车、下单结算、支付功能,因为这是用户购买过程中最核心的功能。而售后退款相关流程可以放到后续版本,因为在初期售后退款功能并不是很迫切,只影响部分用户,且存在替代方案——人工处理的方式。当用户规模达到一定程度后,再做相关功能的开发。

满足需求的方法?

相信大家都去海底捞吃过火锅,每次去总是人满为患,假设你是海底捞的店长,你怎么解决海底捞排队的问题?满足用户快速吃饭的需求。

1.改变现状

我们能想到最直接的办法,就是扩大门店,增加更多的桌子和服务员。但性价比很低,一是扩张需要成本,需要额外负担装修成本、服务员工资;二是在非吃饭高峰期造成资源浪费,大面积的区域没有使用,部分服务员无事可做。

2.降低期望

在海底捞排队取号时,通常服务员都会告诉你前面还有多少桌,并告诉你大概的排队时间。但这个预估的排队时间通常都比实际等待时间要长一倍到两倍,虽然会吓跑一部分顾客,但留下来排队的顾客体验会好很多。本来以为要排 1 个小时,实际上只排了半个小时,内心会有小小的惊喜:海底捞效率还挺高的。

3.转移需求

现在海底捞的做法就是在排队区域准备一些小零食、水果和小游戏,让顾客一边吃东西玩游戏,一边排队。一方面通过零食和小游戏分散顾客的注意力,缓解顾客排队等候的暴躁情绪;另一方面通过零食和小游戏将大部分顾客牢牢的锁在海底捞,提高订单转化率。

研发与产品的爱恨情仇,本是同根生相煎何太急

经常会在网上看到程序员吐槽产品经理的各种段子、漫画和文章,平时工作中也遇到不少。分析下来,矛盾的根源在于:

  • 频繁变更需求
  • 低估研发成本
  • 需求变更没有通知到位

产品经理防“群殴”指南

image__2_

需求频繁变更,其实说明产品经理在分析需求时缺乏思考,没有多想几个为什么。

不要用战术上的勤奋掩饰战略上的懒惰。

经常有朋友问我:做产品经理和做研发有什么区别?

最大的区别在于研发 80% 的时间用于执行,而产品 80% 的时间用于思考和沟通。

1.考虑未来三个月内的扩展性

在做一些比较大的功能模块时,不能只考虑第一个版本的规划,还需要考虑未来两到三个版本的功能。可以将版本规划提前告知研发团队,让他们了解未来产品要做成什么样子,哪些功能必须要做,哪些功能可能要优化。这样研发同学就可以提前做相关技术调研,规划技术架构,提高代码的可扩展性。

2.考虑常见的特殊情况

最常见的情况,调后台接口,展示列表数据。此时就要考虑很多种特殊情况:

  1. 没有网络
  2. 网络条件不好
  3. WiFi网络环境和非WiFi网络环境
  4. 没有数据
  5. 少量数据
  6. 大量数据

再比如,电商业务中最常见的订单,需要梳理整个过程中的状态,并画出各个状态之间的转化示意图。只有把所有状态都列清楚,才能确认逻辑是否完善,让研发能更好的实现订单流程。

3.不要想满足所有人的需求

相信大家也知道夫妻骑驴的故事,在这个故事中,无论这对夫妻采用何种方案,总会有批评的声音。

image__3_

做产品也是同样的道理。比如,针对患教资料是否显示原创医生的个人信息。部分医生认为应该显示,理由一:这是医生花费大量时间和精力编写的文章,要尊重医生的知识产权;理由二:通过分享患教资料扩大知名度,让更多的陌生患者添加自己;另一部分医生却强烈要求隐藏原创医生的个人信息,这部分医生认为自己分享了其他医生写的患教资料,相当于向自己的患者介绍其他医生,为其他医生做推广。

无论是否显示原创医生的个人信息,总会有部分医生不满。你不可能满足所有用户的需求,在这种情况,坚持做自己认为对的事情就好。

4.MVP( Minimal Viable Product 最小可行性产品)

产品迭代路径应该按照 MVP 原则,先向用户提供能用的产品,再根据用户需求,逐步提供更好用的产品。而不是一上来,就规划一个非常完美的方案,然后花上好几个月甚至一两年来实现,说不定等你实现所谓“完美”的产品,用户需求早已发生翻天覆地的变化。

image__4_

5.适当了解一些技术,适当看一些技术文档

在做患者端小程序前,先查看小程序接口文档,了解以下问题:

  1. 小程序能实现是否支持语音、视频、扫一扫功能
  2. 小程序和公众号如何相互跳转
  3. 小程序通知下发机制
  4. 如何配置 webview 业务域名
  5. webview 组件如何实现微信支付

这样才能规划小程序做哪些功能;如何实现小程序和公众号之间相互引流;如何通知用户;遇到非业务域名如何处理;如何在 webview 中调用小程序微信支付。

之前听过一句玩笑话。

产品经理懂技术等于流氓会武功?

流氓会武功的话,对付一般的小警察还是很有优势的;但如果流氓因为自己会武功,而无法无天,自以为是;等遇到狙击手的时候,直接一枪就被放到了好吗。

同样的道理,产品经理不能仗着自己懂一点技术,就对研发同学指手画脚。懂技术是为了排除需求中的隐患,更好的和研发沟通,提升工作效率。了解一些基础技术架构即可,不要沉迷于技术实现,否则,就是本末倒置。

6.重视文档更新

这个不解释。

研发防“入坑”指南

每次版本总结,总是会有研发表示被产品经理“坑”惨了:需求变更太频繁,需求不靠谱,需求解读错误,需求不明确……除去市场、产品本身存在的问题,大部分问题其实是沟通问题。

1.正确看待需求变更

一提到需求变更,一些研发会皱着眉头改代码,心里 mmp ,怎么又 TM 改需求啊;一些研发会让产品经理签字画押,作为日后打脸的呈堂证供;还有一些研发会直接拒绝,要改你来改啊,老子就不改……但事实上,业务稍微复杂一点的产品都会有需求变更,要求产品没有需求变更,相当于要求研发写的代码没有 bug 。我相信没有一个研发敢摸着良心保证自己的代码没有一个 bug 。

从另一个角度思考,需求变更其实是一件好事。正因为有需求变更,才能体现研发的价值:如果没有需求变更,80% 的程序员都要失业了。正因为有需求变更,研发的工作更具有挑战:如何设计更合理的架构,覆盖更多的业务场景,支持更复杂的业务流程。

2.先考虑需求是不是合理,是不是有更好的解决方案

看到这里,有部分研发同学认为,我为什么要考虑这些,这是产品经理的工作,不是研发的工作。这的确不是研发的工作,但实现这些需求却是研发的工作。为了避免自己被“坑”,如果有更好的解决方案,完全可以提出来,和产品经理一起讨论。

也许你们会讨论出一套性价比更高的方案:一方面满足产品需求,一方面减少自身的开发工作量。也许你们讨论过程中发现遗漏了某个分支的处理流程:一方面发现了产品设计的漏洞,一方面减少了 bug 数量提高代码质量。

3.有疑问,有困难一定要尽早提出,不要自己一个人默默的解决

例如,某个功能实现起来有技术难度,此时,研发和产品经理可以沟通讨论出更好的方案;某个模块功能太多研发时间太长,此时,产品经理可以根据实际项目情况,砍掉部分优先级低的需求,或者申请适当延长研发周期。千万不要自己一个人死扛,默默的处理遇到的所有问题,最终产品验收时,发现偏离了产品需求,吃力不讨好。

在每个版本开发前,产品经理都会先和各组研发 leader 进行小范围的需求评审,之后和后台研发同事进行第二轮需求评审,最后和所有相关研发同事进行最终需求评审。在这个过程中,产品经理逐步完善产品设计。前期沟通的越清楚,后期沟通的成本就越小,产品需求变更的情况也越少。

4.产品经理做的不好的地方,可以当面指出

在产品研发的过程中,产品经理接触最多的就是研发,听到最多的就是研发同事对产品经理的吐槽与抱怨。吐槽需求不靠谱,吐槽产品原型有漏洞,吐槽产品经理低估研发成本……吐槽产品经理也成为研发日常工作的一部分,我也相信这些槽点三天三夜也吐不完。

面对这些吐槽与抱怨,任何一个有职业素养的产品经理都会认真的听取意见。毕竟产品经理的部分工作就是收集用户建议、跟踪用户反馈、持续提高用户体验,而研发其实是产品经理最重要的用户。

没有什么事情,是一顿火锅解决不了的。如果有,那就两顿!

相信大家都知道瞎子和跛子的故事:一个瞎子和一个跛子,被困在火场中,眼看着要被烧死了,瞎子背起跛子,跛子指路,终于从大火中死里逃生。

如果说创业就像在火场中逃生,九死一生。那么产品经理就是一个跛子,能看见前进的方向,但没有行动能力;研发是一个瞎子,有行动能力,但看不见前进的方向;如果研发和产品经理之间相互猜忌,埋怨,最终可能是死路一条。只有两者相互信任,才有可能活着走出火场。

最后,希望研发同学和产品经理能够相互信任、和谐沟通,一起愉快的玩耍。

0

技术选型的艺术

什么是技术选型

技术选型对于广大程序员,特别是互联网公司的技术负责人或者架构师来说,一定不陌生。小到日常开发中的一个工具库的选择,大到整个系统语言、架构层面的选择,都是技术选型的范围。今天我们就简单聊聊技术选型。

一般而已,我们会碰到的技术选型,可以分为以下几类:

  • 基础设施选型:云平台或IDC、编程语言、数据库等。
  • 框架和库的选型:前后端的开发框架、核心类库等。
  • 中间件选型:负载平衡、消息中间件、缓存中间件等。
  • 第三方服务选择:第三方的推送、短信等。

实际上,我们常常面临的不是单个技术的选型,而是对于一个项目所涉及的一整套的技术、方案、规范或者产品的选型。这就需要我们更仔细的去权衡各种技术、各种组合的利弊,作出取舍。

技术选型,其实本质上就是一种架构决策。《系统架构》这本书里将架构决策总结成了六种模式,包括选项、筛选、指派、分区、排列和连接。而技术选型明显属于其中的选项(Decision-Option)模式,也即每个决策都是一套离散选项,从中选择一个合适的。

影响技术选型的因素

项目因素

首先考虑的是项目。

我们要明确项目本身的性质,包括项目的规模、重要程度、时间要求等等。如果是一个小规模的实验性项目,那么尝试一些新技术也未尝不可。如果时间要求非常紧,那可以考虑基于开源或商用的程序修改。淘宝当年上线时候,就是购买了一个商用程序后修改的。另外,项目的成本和预算也是必须要考虑的。如果预算足够,我们也可以考虑购买商用程序或者第三方服务。

项目的需求也会影响甚至限制技术的选型。特别要注意的是那些非功能性的需求,例如对并发性、实时性、可用性、数据一致性、安全性等方面的需求,往往对技术方案和选型有很大影响。例如支付相关的应用,对数据的一致性有非常高的要求,那核心的支付数据的存储,就会倾向选择 Oracle、PostgreSQL 这种强一致性的数据库,而不会去选 MongoDB(虽然据说马上也要支持事务了)。

团队因素

其次要考虑的是团队,也就是说人的因素。

技术选型一定要考虑当前团队人员的技术组成。对于一些比较基础的技术的选型,比如语言和框架、数据库等等,往往最合适的选择就是团队最熟悉的技术。如果打算选择不同的技术的话,那就要考虑下团队是否有人能够 Hold 住了。另一个必须要考虑的是招聘,如果使用的是比较小众的技术,那么当需要扩充团队的时候,招聘人员会比较困难。我们杏仁在这方面就取了一个巧,最开始我们用的是基于 JVM 的 Scala 语言,但是我们招聘的时候,都是打的 Java 工程师的幌子。

团队的发展时期对于技术选型也会有一定影响。对于早期团队,大多数员工都是喜欢创新、愿意承担风险,那么可以选择一些相对较新、有挑战的技术;团队发展到一定阶段,那就要开始考虑团队效率、开发规范等因素,此时往往会选择一些比较大众的、经过验证的技术。

我加入现在的公司时,我们正好开始转型。原有的产品是基于 Web 的网站,我们打算开发一个移动 APP。我们原先后端应用使用的是 Scala 语言和 Play 框架,当时面临的一个问题是,新的产品后端应该选择什么技术?虽然也考虑过 Java,但因为时间很紧,我们还是选择了团队最熟悉的 Scala。但后面我们开始服务化的时候,就选择了 Java。

还有一点就是,虽然技术选型需要考虑团队人员的喜好,但千万不要因为某几个人的个人喜好,来决定技术的选型。还是通过细致的分析和实验来进行选型。而决策者也需要看的更长远一些,推动团队技术向前发展。

技术因素

技术选型当然也必须考虑技术因素,例如技术特性(易用性、可维护性、可扩展性、性能等)、技术成熟度、社区活跃度、架构匹配和演化等等。

技术特性往往是大家都会去关注的,相信不必多说。但是注意不要只是浏览网上看到的各种分析,对于重要的特性,还是需要去亲自实验一下或者做一些测试,有第一手的感觉。另外,这些特性往往不可兼得,所以我们需要对照项目因素和团队因素来进行取舍。

技术的发展有其内在趋势,Gartner 每年都会发布一个 Hyper Cycle,标记各种技术的发展阶段;另外一个很好的参考是 ThoughtWorks 的技术雷达。我们可以根据项目、团队等因素去选择不同成熟度的技术和产品。但是对于核心项目,最好还是选择足够成熟的技术,切忌盲目追求新技术。

对于比较重要的应用,我们一般会选择比较大众的技术,因为使用的人多了,也从某种程度上说明这个产品是比较优秀的,并且招聘也会更容易些。这方面可以参考的有 Github 的 star 数、Google Trends、百度指数等等。社区活跃度也是必须要考虑的因素,你一定不会想用一个没有人维护,提了问题也没人回复的技术产品吧。

最后还需要考虑技术产品和当前架构的匹配程度。一个团队的技术栈太过散乱,对开发和运维会是很大的压力,甚至影响系统的稳定性。例如我们当前的数据库使用的是 MySQL,而一个技术产品如果要求必须引入 MongoDB,那我一定会多想一下,我们多维护一个 MongDB 是不是值得。另外,如果大家对自己架构的演化有一定规划,那也要考虑引入的新的技术产品和未来的架构是否能够匹配。

其他因素

最后,可能还会有一些额外的因素要考虑。比如许可协议的问题,前段时间闹得沸沸扬扬的 React Native 许可协议事件,相信大家记忆犹新。还有一些公司,可能对于使用第三方的程序或者服务,有一些原则甚至规范,那也一定要注意参考。另外,在稍大一些的公司里可能还会涉及一些派系之争,这里就不多说了。

如何进行技术选型

我们上面已经列出了很多技术选型需要考虑的因素,那么到底如何进行技术选型呢?大致上可以分为以下几个步骤:

  1. 首先要明确选型的需求和目的,最好能列出必须要考虑的各种因素以及评判标准。
  2. 然后就可以开始寻找候选技术或产品了。这时范围可以尽量广一些,搜集尽可能多的候选技术和产品。
  3. 其次就可以进行初步筛选。把一些由于各种限制无法选择的、或者明显不可能的技术或产品排除,筛选出 3 个左右备选方案。
  4. 再然后就要做一些详细的调查和分析了。可以列一个表格,把备选方案、以及需要考虑的因素放到表格的横向和纵向中去,一个个进行评估的分析。此时可能会需要做一些小 Demo 验证可行性,或者做一些性能测试、压力测试等等。必要的话可以在表格里给每一个因素打分。
  5. 最后,对分析结果进行评审,作出最后决定。

技术选型分析表,每一格里可以有具体的打分,也可以有优势劣势的评价。

候选A 候选B 候选C
团队
技术成熟度
性能
架构一致性
...

当然,小的不太重要的技术选型也不一定要这么麻烦,而重要的技术选型则可能要反复各个步骤多次。

技术选型的几个注意点

  • 一定要进行可行性分析,如果不太确定,做个 Demo 验证一下。如果项目进行到一半,发现原来设想的某个方案不可行,那会是非常痛苦和浪费时间的事情。
  • 不要有思维定势,习惯性的使用某些技术,比如服务化就用 Dubbo、缓存就用 Redis,具体问题要具体分析。也不要赶时髦,在重要的项目上使用太新的不够成熟的技术。
  • 随着业务发展,很多架构需要不断升级,所以一定要考虑未来如果要替换某项技术,是否会很麻烦。可以选择符合一些标准的技术或产品,或者在应用中部署一个适配层,方便未来适配其他技术。
  • 架构应该尽可能统一,一个领域避免引入太多相同功能的技术产品。

总结

最后,大家其实会发现,技术选型既是一种科学、又是一种艺术,有时候并没有对错之分。最后面临两难选择的时候,还是需要决策人拿出勇气,拍板决定,坚定的去推进。

0

服务网格:微服务进入2.0时代

微服务自2014年3月由Martin Fowler首次提出以来,在Spring CloudDubbo等各类微服务框架的帮助下,以燎原之势席卷了整个IT技术界,成为了最主流的分布式应用解决方案。但仍然还有很多问题没有得到根本性的解决,比如技术门槛高、多语言支持不足、代码侵入性强等。如何应对这些挑战成为了下一代微服务首要回答的问题。直到服务网格(Service Mesh)被提出,这一切都有了答案。

1 微服务之殇

时光回到2017年初,那时所有主流的微服务框架,不管是类库性质的FinagleHystrix,还是框架性质的Spring Cloud、Dubbo,本质上都归于应用内解决方案,都存在以下三个问题:

  • 技术门槛高:随着微服务实施水平的不断深化,除了基础的服务发现配置中心授权管理之外,团队将不可避免的在服务治理层面面临各类新的挑战,包括但不限于分布式跟踪、熔断降级、灰度发布、故障切换等,这对团队提出了非常高的技术要求。

service-governance

图片出处:Service Mesh:下一代微服务

  • 多语言支持不足:对于稍具规模的团队,尤其在高速成长的互联网创业公司,多语言的技术栈是常态,跨语言的服务调用也是常态,但目前开源社区上并没有一套统一的、跨语言的微服务技术栈。
  • 代码侵入性强:主流的微服务框架(比如Spring Cloud、Dubbo)或多或少都对业务代码有一定的侵入性,框架替换成本高,导致业务团队配合意愿低,微服务落地困难。

这些问题加起来导致的结果就是,在实施微服务的过程中,小团队Hold不住,大公司推不动。

2 另辟蹊径

如何解决上述三个问题呢?最容易想到的是代理模式,在LB层(比如NginxApache HTTP Server)处理所有的服务调用,以及部分服务治理问题(比如分布式跟踪、熔断降级)。但这个方案有两个显著的缺点,第一,中心化架构,代理端自身的性能和可用性将是整个系统的瓶颈;第二,运维复杂度高,业务团队笑了,运维团队哭了。

难道这就是桃园吗?

服务网格(Service Mesh)应运而生!自2016年9月Linkerd第一次公开使用之后,伴随着LinkerdEnvoyIstioNGINX Application PlatformConduit等新框架如雨后春笋般不断涌现,在微服务之后,服务网格和它的边车(Sidecar)模式引领了IT技术界2017一整年的走向。

3 服务网格

3.1 元定义

首先,我们来看一下服务网格的提出者William Morgan是如何描述它的。

A service mesh is a dedicated infrastructure layer for handling service-to-service communication. Consists of a control plane and data plane (service proxies act as "mesh"). - William Morgan, What's a Service Mesh? And Why Do I Need One?

上面这段话非常清晰的指明了服务网格的职责,即处理服务间通讯,这正是服务治理的核心所在。而a dedicated infrastructure layer这几个单词将服务网格和之前所有的微服务框架(framework)划清了界限,也即服务网格独立于具体的服务而存在,这从根本上解决了前文提到的老的微服务框架在多语言支持和代码侵入方面存在的问题。并且,由于服务网格的独立性,业务团队不再需要操心服务治理相关的复杂度,全权交给服务网格处理即可。

那你可能会问,这不跟之前提到的代理模式差不多吗?区别在于服务网格独创的边车模式。针对每一个服务实例,服务网格都会在同一主机上一对一并行部署一个边车进程,接管该服务实例所有对外的网络通讯(参见下图)。这样就去除了代理模式下中心化架构的瓶颈。同时,借助于良好的框架封装,运维成本也可以得到有效的控制。

linkerd-service-mesh-diagram

图片出处:What's a Service Mesh? And Why Do I Need One?

3.2 演化史

追本溯源,服务网格从无到有可分为三个演化阶段(参见下图)。第一个阶段,每个服务各显神通,自行处理对外通讯。第二个阶段,所有服务使用统一的类库进行通讯。第三个阶段,服务不再关心通讯细节,统统交给边车进程,就像在TCP/IP协议中,应用层只需把要传输的内容告诉TCP层,由TCP层负责将所有内容原封不动的送达目的端,整个过程中应用层并不需要关心实际传输过程中的任何细节。

pattern-network

pattern-library

pattern-sidecar

图片出处:Pattern: Service Mesh

3.3 时间线

最后,再来回看一下服务网格年轻的历史。虽然服务网格的正式提出是在2016年9月,但其实早在2013年,Airbnb就提出了类似的想法——SmartStack,只不过SmartStack局限于服务发现,并没有引起太多关注,类似的还有Netflix的Prana和唯品会的OSP Local Proxy。2016年服务网格提出之后,以Linkerd和Envoy为代表的框架开始崭露头角,并于2017年先后加入CNCF基金(Cloud Native Computing Foundation),最终促使了一代新贵Istio的诞生。2018年,Istio将发布1.0版本,这也许意味着微服务开始进入2.0时代。

history

图片出处:Service Mesh:下一代微服务

4 参考

0

如何写好产品中的提示文案

产品中的提示文案往往并不显眼,许多人投入精力优化大标题、Slogan,却忽视了产品中无处不在的提示文案。无论是点击按钮的反馈,还是针对一项功能的注释说明,这些文字反而是和用户沟通最频繁的存在,在潜移默化中输出着整体的品牌形象,影响了用户对产品的认知。

不同功能模块往往由不同的人负责,而每个人都有各自的语言风格,长期下来,就会在同一个产品中形成不同风格的提示文案,造成一定的割裂感。因此,有必要提前达成共识,以一个共同的准则去输出产品中的提示文案。

我们往往可以从发声者(Voice)、语言规范(Language)、语调情感(Tone)三个层面来制定一个完整的准则。发声者强调确认产品文案的主体人格,究竟是谁在和用户沟通;语言规范则帮助我们形成简单、易懂、明确的表达方式;而语调情感则是在主体人格确立的前提下,进一步探讨文案的情感表达,是偏活泼或沉稳,偏中立或引导。

一、发声者

发声者是谁

我们首先需要确定的是,在不同情景下,究竟是谁在为产品文案发声。往往我们有三种选择:

  • 一个没有存在感的客观形象:这可能是大多数产品在大多数情景下的选择,将产品认为是一个整体性的客观主体,它尽可能地降低自身的存在感。在这种情境下,产品中多使用「你」作为沟通语言,仿佛是有一个人在和你对话,但这个人并不显露身份,也因此尽可能地避免使用「我们」。
˟需要我们帮你撤回该消息吗?
✓你确认撤回该消息吗?
  • 一个有鲜明人格形象的客观形象:在人格化运营尝试中,我们往往赋予产品以一个鲜明的人格形象,如小管家、小帮手,以拉近和用户的距离感。我们往往会赋予这个形象以具体的姓名、语气、常用语,来进一步强化它的人格特征。
˟你的搜索结果已保存到收藏夹
✓小度已经把搜索结果塞进收藏夹啦
  • 将用户作为主体代入:也有部分产品,尤其是游戏类的应用,往往会舍弃客观形象的存在,而为了给用户营造更强的浸入感,完全使用用户作为表达的主体,仿佛每一句话,都是用户自己的心声。
˟你要攻打这座城池?
✓我要攻打这座城池

不同发声者,可以在一个产品中共存

根据场景不同,不同的发声者,是可以在一个产品中共存的。例如,一个选择客观形象作为主要发声者的产品,在一定情境下,也可以完全转换到第一人称的表达。在某些情境下,我们希望能引起用户强烈的认同感,或者明确操作的自主自愿性,都可以临时转换到第一人称「我」的表达。

˟你同意上传个人资料
✓我同意上传个人资料

不要在一个页面或流程中混入不同的发声者

在同一个表达、页面或流程中,混入不同的发声者,会造成强烈的割裂感,甚至增加理解的复杂度。因此,应该尽可能保持主体的一致性。

˟去「我的设置」修改你的主页可见性
✓去「我的设置」修改我的主页可见性

又比如,在上述同意上传个人资料的操作下,如果想要告知用户,会有专人来审核,下面一行的小字,应该避免如下表达:

˟我们将安排专人审核你的资料,并通知你结果

这样的表达,不仅用户的代入感从「我」又切换回了「你」,在理解中,还额外增加了发声者「我们」和第三方「专人」。我们可以尝试这样的提示:

✓资料经审核后,会通知我结果

二、语言规范

完整的语言规范涉及到了表义规范、写作规范、标点规范、用词规范等等,不一而足,在这里没法面面俱到,就挑几个最重要的说。

提高信息熵

我们常常说,用户没有耐心去仔细阅读我们精心放置的提示文字。很多时候,是我们写得太过啰嗦了。一般来说,在手机屏幕上,超过两行的提示文字,就已经让人不想阅读了。

在写作提示文字时,我们必须尽可能提高信息熵,即在表达信息量相同的情况下,尽可能减少内容量。要做到这一点,不必过于拘束,而可以先把你想表达的意思写下来,再开始精简。

你可能会发现,许多时候可以精简的内容量是惊人的。假设现在我们想告诉用户,他的消息已经发送成功了,我们可能会这么写:

˟你的消息已经发送成功

显然,「你的消息」过于啰嗦了,在实际操作中,用户刚刚发送完一条消息,自然能和这条提示文字对应上,我们可以精简成:

˟已经发送成功

现在,我们再来想一下,我们提示用户这条信息已经发送出去了,成功应该是大概率的情况,如果发送失败,显然我们需要更强烈的引导和提示。那么,「信息已经发送」在理解上和「信息已经发送成功」应该是无差别的。因此,它还可以进一步缩减成为:

˟已经发送

更进一步的,在这句话中,「已经」作为状语,缩写成「已」,不仅语义是一致的,在语感上也没有任何区别(「可以」缩写为「可」常常会产生语感上的区别)。因此,这个提示可以进一步变成:

✓已发送

你看,从最初的 10 个字,已经变成了最后的 3 个字。事实上,只要我们认真审视每句话里面的主谓宾定状补,就总能发现各种精简后并不影响理解和表达的成份。

从目的出发

向用户提示时,就像我们和人沟通一样,表达目的应该先行。用户不应该需要阅读大段的操作性说明,来了解最终可以实现什么目标。相反,应该是获知目标后,再决定是否需要进一步了解。

˟只要提现金额大于 1,000 元,就能当日到账;
✓若想当日到账,提现金额须大于 1,000 元;

使用用户的语言

使用用户的语言和他们沟通,避免使用内部代号、技术性描述等语言。

˟对方服务器没有响应,将重试 POST 该消息,请等待返回结果;
✓发送失败,将尝试重发,请等待;

使用主动语态

在写作提示文字时,如果不是出于特别强调的目的,尽量使用主动语态。简单一点来说,在你的提示文字中,少出现「被」字。

˟密码被输入后,将向对方转账
✓输入密码以完成转账

使用阿拉伯数字

阿拉伯数字在视觉上更加醒目,试试比较下面两句话:

˟若想当日到账,提现金额须大于一千元;
✓若想当日到账,提现金额须大于 1,000 元;

三、语调情感

在明确发声者和语言规范后,我们还应该进一步丰满其形象,通过设立语气、表达方式、常用语等特征,最终映射到品牌或产品形象上。感受一下,下面三种表达,在用户脑海中呈现出的形象,是截然不同的。

小度已经把搜索结果添加到收藏夹
小度已经把搜索结果添加到收藏夹啦
小度已经把搜索结果塞进收藏夹啦

显然,第一种给人的感觉是严肃、平淡的,第二个仅仅通过添加了语气助词「啦」,就让整体形象稍显活泼,第三个在使用了「塞进」这样的表达后,在活泼之余,又增添了一份趣味性。

语调情感并没有绝对的好坏,需要根据产品面向的用户群体和使用场景,来差异化地制定。不过,有一些基本通用的准则,可以供你参考:

产品和用户的关系定位

在产品中指代用户时,使用「你」还是「您」,实际上是产品和用户的关系定位决定的。

在杏仁医生中,由于我们的用户群体是医生,抱以最大的尊重,我们对用户的称谓,一律都是使用「您」的。而在服务患者的微信公众号上,由于平台和用户之间更多的是平等的关系,我们则使用「你」来称呼。

除了人称代词外,确立了关系定位后,还会影响谦词、敬词的使用。感受一下:

对不起,没有找到对应结果,请稍后再试
没有找到对应结果,请稍后再试
没有找到对应结果,你可以稍后再试

使用积极的表达方式

在语言规范中,我们强调多使用主动语态。而在语调情感中,我们则应该多使用积极的表达方式,从肯定视角进行描述。感受一下:

˟上传的头像不要超过 2MB;
✓上传的头像须在 2MB 以内;

除了避免使用否定视角,我们还应该尽可能地突出正面意义,避免恐吓用户。例如:

˟常时间开启高性能模式会加快电量消耗,直至关机
✓为了节省电量,你可以关闭高性能模式

慎用感叹号

感叹号实际上是一个非常强烈的情感表达,你可以想像着如果和用户面对面,会不会这么说话。比如,在针对新用户的提示中,我们使用如下表达来展现我们的热情洋溢,是可以接受的:

✓欢迎来到杏仁医生!

不过,如果是错误提示、操作引导,滥用感叹号,就不免让人觉得有喝斥责怪之感,感受一下:

˟页面已经到底了!
0

Facebook、Google、Amazon 是如何高效开会的

会议是工作中绕不开的一部分,许多人都听说过,在一项研究中发现,语言在我们的沟通中只占了 7% 的比例。虽然这个研究结果仅仅是面向单个字眼的沟通,在现实中比例不至于这么夸张,但不可否认的是,冰山下的许多信息,都是依靠语调、身体语言来传递的。这也是为什么,即使在线沟通如此多样和便捷,会议依旧不可被取代。

然而,许多人却会对会议有抵触情绪。冗长的节奏,即兴的跑题,敌对的氛围,模糊的结论……这些都让会议要不然变得漫长而无效,要不然变得沉闷而无趣。那么,如果才能开一场高效的会议呢?一起来看看科技巨头,如 Facebook、Google、Amazon 等这些公司,是如何高效开会的。

Facebook

  • 明确会议类型:在 Facebook,CEO Zuckerberg 要求所有人在开会前,都先想好一个最基础的问题:本次会议的目标是什么?在他看来,会议可以分为两大类:决策型会议、讨论型会议。前者必须输出一个明确的决定,如项目评审会、预算审批会,而后者则可能是召集所有人一起讨论问题,共享信息,如晨会、头脑风暴、项目沟通会等。
  • 为会议制作进度表:把会议议题制作成一张进度表,每讨论完一个议题,就打一个勾,这是 Facebook 的 COO Sheryl Sandberg 的建议。一方面,确保每一个议题都已经有了一个明确的结果;另一方面,如果你提前勾完了这份进度表,就让会议提前结束。
  • 在中午 12 点开站会:Facebook 的研发经理 Mark Tonkelowitz 往往会在中午开一个 15 分钟左右的站会,所有人一会都要去吃饭,自然不会愿意花时间跑题。

image

Google

  • 保持会议精简:Google 的历任高管都曾强调会议精简的重要性。想要让会议精简,首先必须控制人,然后是控制时间。2011 年 Larry Page 回归 Google 重新出任 CEO 时,甚至要求全公司每个会议最多不超过 10 个人。而曾在雅虎、Google 担任高管的 Marissa Mayer,她要求会议尽量保持在 5-10 分钟之间,快速讨论,快速结束。
  • 提前制定会议议程:Google 曾经的 CEO Eric Schmidt 在《How Google Works》一书中提到过,一定要提前 24 小时,将会议议程发给所有的参会人。议程应该包含会议目标、参会人、待讨论的事项等内容。
  • 每个会议都有一个 Leader:每一个会议上都需要有一个 Leader 负责做决定,并且在会后 48 小时内,将所有的结论总结成邮件发送给参会人与相关方。
  • 紧急决定不需要等待会议:在 Google,会议是为了得出结论的。不要让决定等待会议,如果你马上需要做一个紧急决定,要不就跳过会议直接做决定,要不就立即召集会议。
  • 用数据替代争论:在会议中难免会陷入分歧,不同的人持有不同的观点,这时候会议往往会陷入无休止的争吵和讨论。Google 强调在这类问题上,尽可能抛弃主观色彩,而使用数据说话。
  • 准备一个计时器:Google Ventures 的合伙人 Jake Knapp 推荐任何会议都应该有一个计时器,而且是实体的计时器。他特别推荐 Time Timer的计时器,表盘上红色的区域会随着时间的流逝而逐渐减少,更让参会人有时间的紧迫感。

image

Amazon

  • 只准备两份批萨:对于如何控制会议规模,Amazon 的 CEO Jeff Bezos 曾有一个非常有趣的衡量标准:一场会议的人数,订两份批萨就够了。如果超出这个规模,说明这个会议有太多的人参加了。
  • 永远准备一张空椅子:Jeff Bezos 还做过一个有趣的提议:永远在会议室准备一张空的椅子来代表顾客。Amazon 的核心文化理念之一,就是从各个方面不断完善和提升顾问体验。这张空椅子就是一个符号性的象征,提醒每个人不要忘记这一点。

xMentalHelpChat-iStock516454908-Gestalt-Therapy-The-Empty-Chair-Technique-1024x585.jpg.pagespeed.ic.KFNGU7vI9a

可以看到,无论是 Amazon、Facebook 还是 Google,这些公司对会议都有一些共同的要求,例如只邀请必要的人,控制会议时长,事先准备议程,事后有结论等等。你也可以为自己的工作环境制定一些会议准则,来更高效地开会。

参考文章:
1. http://www.businessinsider.com/googles-rules-for-a-great-meeting-2014-9
2. https://www.inc.com/larry-kim/jeff-bezos-surprising-meeting-strategy.html
3. http://www.businessinsider.com/this-is-how-larry-page-changed-meetings-at-google-after-taking-over-last-spring-2012-1
4. https://www.getminute.com/how-to-run-a-meeting-like-google-apple-amazon-and-facebook/
5. https://qz.com/94701/top-business-leaders-meeting-tips/

1+

谈谈到底什么是抽象,以及软件设计的抽象原则

我们在日常开发中,我们常常会提到抽象。但很多人常常搞不清楚,究竟什么是抽象,以及如何进行抽象。今天我们就来谈谈抽象。

什么是抽象?

首先,抽象这个词在中文里可以作为动词也可以作为名词。作为动词的抽象就是指一种行为,这种行为的结果,就是作为名词的抽象。Wikipedia 上是这么定义抽象的:

Conceptual abstractions may be formed by filtering the information content of a concept or an observable phenomenon, selecting only the aspects which are relevant for a particular subjectively valued purpose.

也就是说,抽象是指为了某种目的,对一个概念或一种现象包含的信息进行过滤,移除不相关的信息,只保留与某种最终目的相关的信息。例如,一个皮质的足球,我们可以过滤它的质料等信息,得到更一般性的概念,也就是。从另外一个角度看,抽象就是简化事物,抓住事物本质的过程。

需要注意的是,抽象是分层次的。还是用 Wikipedia 上的例子,以下是对一份报纸在多个不同层次的抽象:

  1. 我的 5 月 18 日的《旧金山纪事报》
  2. 5 月 18 日的《旧金山纪事报》
  3. 《旧金山纪事报》
  4. 一份报纸
  5. 一个出版品

可以看到,在不同层次的抽象,就是过滤掉了不同的信息。这里没有展现出来的是,我们需要确保最终留下来的信息,都是当前抽象层需要的信息。

生活中的抽象

其实我们生活中每时每刻都在接触或者进行各种抽象。接触最多的,应该就是数字了。其实原始人类并没有数字这个概念,他们可能能够理解三个苹果,也能够理解三只鸭子,但是对他们来说,是不存在数字“三”这个概念的。在他们的理解里,三个苹果和三只鸭子是没有任何联系的。直到某一天,某个原始人发现了这两者之间,有那么一个共性,也即是数字“三”,于是就有了数字这个概念。从那以后,人们就开始用数字对各类事物进行计数。

赫拉利在《人类简史》里说,人类之所以成为人类,是因为人类能够想象。这里的想象,我认为很大程度上也是指抽象。只有人类能够从具体的事物本身,抽象出各种概念。可以说,人类的几乎所有事情,包括政治(例如民族、国家)、经济(例如货币、证券)、文学、艺术、科学等等,都是建立在抽象的基础上的。绘画有一个流派叫抽象主义,很多人(包括我)都表示看不懂,但下面几幅毕加索画的牛,也许能够从直观上让我们更好的理解什么是抽象。

毕加索的牛

科学里的抽象就更广泛了,我们可以认为所有的科学理论和定理都是一种抽象。物体的质量是一种抽象,它不关注物体是什么以及它的形状或质地;牛顿定律是对物体运动规律的抽象,我们现在知道它不准确,但它在常规世界里,却依然是一个相当可靠的抽象。在科学和工程里,常常需要建立一些模型或者假设,比如量子力学的标准粒子模型、经济学的理性人假设,这些都是抽象。甚至包括现在 AI 里通过训练生成的模型,某种程度上说,也是一种抽象。

当然,哲学上对抽象有很多讨论,什么本体论、白马非马之类的,这些已经在本人的理解范围之外了,就不讨论了。

开发中的抽象

现在我们应该能大致理解抽象这个概念了,让我们回到软件开发领域。

在软件开发里面,最重要的抽象就可能是分层了。分层随处可见,例如我们的系统就是分层的。最早的程序是直接运行在硬件上的,开发成本非常高。然后慢慢开始有了操作系统,操作系统提供了资源管理、进程调度、输入输出等所有程序都需要的基础功能,开发程序时调用操作系统的接口就可以了。再后来发现操作系统也不够,于是又有了各种运行环境(如 JVM)。

编程语言也是一种分层的抽象。机器理解的其实是机器语言,即各种二进制的指令。但我们不可能直接用机器语言编程,于是我们发明了汇编语言、C 语言以及 Java 等各种高级语言,一直到 Ruby、Python 等动态语言。

开发中,我们应该也都听说过各种分层模型。例如经典的三层模型(展现层、业务逻辑层、数据层),还有 MVC 模型等。有一句名言:“软件领域的任何问题,都可以通过增加一个间接的中间层来解决”。分层架构的核心其实就是抽象的分层,每一层的抽象只需要而且只能关注本层相关的信息,从而简化整个系统的设计。

其实软件开发本身,就是一个不断抽象的过程。我们把业务需求抽象成数据模型、模块、服务和系统,面向对象开发时我们抽象出类和对象,面向过程开发时我们抽象出方法和函数。也即是说,上面提到的模型、模块、服务、系统、类、对象、方法、函数等,都是一种抽象。可想而知,设计一个好的抽象,对我们软件开发有多么重要。

抽象的原则

那么到底应如何做到好的抽象呢?在软件开发领域,其实早就有 SOLID 等原则,虽然很多人都听说过,但其实真正能理解这些原则的开发者并不多。那么我们就从抽象的角度,再来看下这些原则,也许会有更好的理解。

单一职责原则(Single Responsibility Principle, SRP)

单一职责是指一个模块应该只做一件事,并把这件事做好。其实对照应抽象的定义,可以发现这个原则本身就是抽象的核心体现。如果一个类包含了很多方法,或者一个方法特别长,就要引起我们的特别注意了。例如下面这个 Employee 类,既有业务逻辑(calculatePay)、又有数据库逻辑(saveToDb),那它其实至少做了两件事情,也就不符合单一职责原则,当然也就不是一个好的抽象。

class Employee {
  public Pay calculatePay() {...}
  public void saveToDb() {...}
}  

有些人觉得单一职责不太好理解,有时候很那分辨一个模块到底是不是单一职责。其实单一职责的概念,常常需要结合抽象的分层去理解。

在同一个抽象层里,如果一个类或者一个方法做了不止一件事,一般是比较容易分辨的。例如一个违反单一职责原则的典型征兆是,一个方法接受一个布尔类型或者枚举类型的参数,然后一个大大的 if/else 或者 switch/case,分支里也是大段的代码处理各种情况下的逻辑。这时我们可以用简单工厂模式、策略模式等设计模式去优化设计。

假如说我们用了简单工厂模式,改进了一段代码,重构后代码可能像是下面是这样的。

    public Instance getInstance(final int type){ 
        switch (type) {
            case 1: return new AAInstance;
            case 2: return new BBInstance;
            default: return new DefaultInstance();
        }
    }

有人可能会有疑问,代码里依然还是存在 if/else 或者 switch/case,这不还是做了不止一件事情么?其实不是的,使用了简单工厂模式,其实就是增加了一个抽象层。在这个抽象层里,getInstance 的职责很明确,就是创建对象。而原来分支里的逻辑处理,则下沉到了另外一个抽象层里去了,也就是 Instance 的实现所在的抽象层。

再看下面 Scala 实现的 updateOrder 方法,它似乎也只是做了一件事情:处理订单,那算不算单一职责呢?

protected def updateOrder(t: TransationEntity) = {
  // 1 获取订单
  ManagedRepo.find[Order]("orderNo" -> t.tradeNo).map { order =>
    // 2 检查订单是否已支付
    val ps = SQL("""select statue from Order where id ={id} for update""").on("id" -> order.id).as(scalar[Long].singleOpt).getOrElse(0l)
    if (ps == PAID) {
      throw ServiceException(ApiError.SUBSCRIPTION_UPDATE_FAIL)        
    } else {
      // 3 更新订单信息,标记为已支付            
      val updatedOrder = // 略...
      updatedOrder.saveOrUpdate()
      // 4 生成收入记录
      createIncome(updatedOrder)
    }
  }
}

答案当然是不算,因为很明显,这个方法里面既有业务逻辑的代码,又有数据库处理的代码,这两类应该是在不同的抽象层的。我们把数据库处理的代码抽取出来,下沉到数据层,它就能符合单一职责原则了。

protected def updateOrder(t: TransationEntity) = {
  findUnpaidOrder(rtent.tradeNo).map { order =>
    val updatedOrder = updateOrderForPayment(rtent)
    createIncome(updatedOrder)
  }
}  

开放封闭原则(Open/Closed Principle, OCP)

开放封闭原则是指对扩展开放,对修改封闭。当需求改变时,我们可以扩展模块以满足新的需求;但扩展时,不应该需要修改原模块的实现。

下面两段代码都实现了方形、矩形以及圆形的面积计算。第一种用的是面向过程的方法,第二种用的是面向对象的方法。那么,到底哪一种更符合开放封闭原则呢?

面向过程方法:

public class Square { 
    public double side;
}
public class Rectangle { 
    public double height;
    public double width;
}
public class Circle { 
    public double radius;
}
public class Geometry {
    public double area(Object shape) {
        if (shape instanceof Square) {
            Square s = (Square) shape;
            return s.side * s.side;
        } else if (shape instanceof Rectangle) {
            Rectangle r = (Rectangle) shape;
            return r.height * r.width;
        } else if (shape instanceof Square) {
            Circle c = (Circle) shape;
            return PI * c.radius * c.radius;
        } else {
            throw new NoSuchShareException();
        }
    }
}

面向对象方法:

public class Square implements Share { 
    public double side;
    public double area() {
        return side * side;
    }
}
public class Rectangle implements Share { 
    public double height;
    public double width;
    public double area() {
        return height * width;
    }
}
public class Circle implements Share { 
    public double radius;
    public double area() {
        return PI * radius * radius;
    }
}

估计很多人会觉得面向对象的方式更好,更符合开放封闭原则。但真相其实没那么简单。想象如果我们需要添加一个新的形状,比如说椭圆,那面向对象的实现肯定更方便,我们只需要实现一个椭圆的类以及它的 area 方法。这时候我们可以说面向对象的方法更符合开放封闭原则。

但如果我们需要添加一个新的方法呢?比如说,我们发现我们还需要计算形状的周长。这时候,面向对象的实现似乎就没那么方便了,要在每个类里面添加计算周长的方法。而面向过程的方法,则只需要添加一个方法就行了。这时候,我们反而发现面向过程的方法更符合开放封闭原则。

所以开放封闭其实是相对的,有时候,如何进行抽象,取决于我们对未来最有可能的扩展的预判。

依赖倒置原则(Dependency Inversion Principle, DIP)

依赖倒置原则是指高层模块不应该依赖于低层模块的实现,两者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖与抽象。前面提到,“软件领域的任何问题,都可以通过增加一个间接的中间层来解决” ,DIP 就是最典型的增加中间层的方式,也是我们需要解耦两个模块的最重要的方法之一。

依赖倒置原则的一个例子是 Java 的 JDBC。如果没有 JDBC,那我们的系统就会严格依赖我们使用的那个数据库。这时如果我们想要切换到另外一个数据库,就需要修改大量代码。但 Java 提供了 JDBC 接口,而所有关系数据库的连接库都实现了这个接口,我们的系统也只需要调用 JDBC 即可完成数据库操作。这时我们的系统和数据库的依赖就解除了。除了 JDBC,其实 SQL 本身也是一种依赖倒置的实现。另外一个很典型的例子就是 Java 的日志接口 Slf4j。
依赖倒置JDBC

其实所有的协议和标准化都是 DIP 的一种实现。包括 TCP、HTTP 等网络协议、操作系统、JVM、Spring 框架的 IOC 等等。设计模式里有不少模式,也是典型的依赖倒置,例如状态模式、工厂模式、代理模式、策略模式等等,下图是策略模式的结构图。
Strategy

我们日常生活中也有很多依赖倒置的例子。比如电源插座,家庭的供电只需要提供符合国家标准的电源插座,我们购买电器产品时,就不用担心买回来无法接入电源。汽车和轮胎、铅笔和笔、USB/耳机接口等等,也都是同一思想的体现。

里氏替换原则(Liskov Substitution Principle, LSP)

里氏替换原则是指子类必须能够替换成它们的基类。例如下面这个最常见的例子,Square 可以是 Rectangle 的子类吗?

public class Rectangle { 
    public double height;
    public double width;

    public void setHeight(int height) { ... }
    public void setWidth(int width) { ... }
}

public class Square extends Rectangle { 
    ???
}

虽然几何上说,Square 是一个特殊的 Rectangle,但把 Square 作为 Rectangle 的子类,却未必合适,因为它已经不存在宽和高的概念了。如果一个抽象不能符合里氏替换原则,那我们就需要考虑下这个抽象是不是合适了。

接口隔离原则(Interface Segregation Principle, ISP)

接口隔离原则是指客户端不应该被迫依赖它们不使用的方法。例如下面的 Square 类如果继承了Shape 接口,该如何计算体积以实现volume方法?

interface Shape {
    public function area();
    public function volume();
}
public class Square extends Shape { 
    ???
}

同样,如果一个抽象不符合接口隔离原则,那可能就不是一个合适的抽象。

迪米特法则(Law of Demeter)

迪米特法则不属于 SOLID 原则,但我觉得也值得说一下。它是指模块不应该了解它所操作的对象的内部情况。想象一下,如果你想让你的狗狗快点跑的话,你会对狗狗说,还是对四条狗腿说?如果你去店里买东西,你会把钱交给店员,还是会把钱包交给店员让他自己拿?

下面是一段违反迪米特法则的典型代码。这样的代码把对象内部实现暴露了出来,应该考虑讲将功能直接暴露为接口,或者合理使用设计模式(如 Facade)。

final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();

总结

关于抽象,今天我们就说到这里。不过要注意的是,软件开发并不是仅仅只依靠抽象能力就能完成的,最终我们还是要把我们抽象出来的架构、模型等,落地到真正的代码层面,那就还需要逻辑思维能力、系统分析能力等。以后如果有机会,我们可以继续探讨。

我希望各位看完本文,对抽象的理解能够更加深入一点。我们以奥卡姆剃刀原则来结束吧:一个抽象应该足够简单,但又不至于过于简单。这其实就是抽象的真谛。

2+

喜欢该文章的用户:

  • avatar

苹果在医疗健康领域的三个 Kit

Apple Watch Series 2 发布时,苹果对它进行了重新定位,聚焦在 Fitness 领域。这个转型显然获得了成功,虽然没有公布过官方的销量数据,但根据外界的普遍预测,Apple Watch 今年的出货量已经达到了 1500-2000 万之间。

其实,苹果对于健康领域的兴趣,不仅仅停留在硬件层面上。在软件层面上,近年来也动作颇多。iOS 8 发布时,系统内置了一个名为「健康」的原生应用,并同时发布了面向开发者的 HealthKit;2015 年春季发布会,苹果发布了 ResearchKit;而在去年的 2016 春节发布会上,苹果不仅进一步加强了 ResearchKit,还推出了全新的 CareKit。

三个 Kit,不再傻傻分不清楚

Artboard

HealthKit、ResearchKit、CareKit,这三个 Kit 面向的对象是开发者,它们的存在,大大降低了开发者的门槛。现在,医院里的医生、大学里的医学教授、国际医学中心的科研小组,拥有丰富的临床知识和经验却不一定会编程的他们,现在也有可能做出一个自己的 app 了。三者作为一个有机的整体,相辅相成,不断扩展着健康管理的方式和边界

那么,具体到每一个 Kit,它们是如何各司其职的呢?

HealthKit:提供应用间的健康数据分享标准

HealthKit 作为最早推出的开发套件,实际上也是苹果整个医疗健康产业的基石。试着去打开你手机上的「健康」应用,你可以看到各类健康相关的指标,从基本的健康数据,到生殖健康,再到化验结果、营养摄入……大大小小总共涵盖了数十种指标和体征数据。之所以说 HealthKit 是后面一系列事情的基石,正在于它定义了数据的类型和互相通信的标准。现在,你可以通过将各类第三方健康应用,如 Keep、乐动力、Runtastic,甚至微信运动,它们都可以通过 HealthKit 互相交换、读取、写入健康相关的数据。

image

乍一看这好像也不算什么特别大的事情,实际上如果对医疗行业有稍微深入的认识,你就可以揣测出苹果未来的伟大愿景。现在你去医院看病的时候,基本上所有的病历、开药都已经完全电子化了,没错,医疗健康行业现在可以说是一个信息化的行业,但是,却是一个高度碎片信息化的行业。

如何理解高度碎片化?非常简单,你在一个医院做的检查,得到的化验结果,跑到另一家医院,他们的系统里就没法调取你的化验数据,很多时候你需要重新再来一次。所以,国内外的一些医疗信息化标准的最高等级目前都是七级,都要求实现区域化的医疗信息共享。的确在部分地区形成了区域互通的医疗信息共享,然而,如果想进一步将北京和天津两地的医疗数据打通,那么又是一件麻烦事。每一家医院存储和记录医疗数据的方式、格式都非常不同,仅仅将这些数据形成一套统一可交换的规范协议,就需要花费不少的时间去改造。何况,这里提到的碎片化还只是不同机构间的数据无法互通,另一个更大的问题在于,由于患者的健康数据无法连贯保存,无法形成完整的数据轨迹。

显然,这种方式的变革太慢,苹果正是希望通过 iOS 在全球的影响力和普及程度,从数据的角度入手,首先解决医疗中的老大难问题:形成一套可互通互换的数据格式协议。现在,或许你看到的只是应用和应用间的数据共享,但未来呢,应用和可穿戴硬件之间,和医疗器械之间,和医疗信息系统之间……苹果的 HealthKit,就是连接这一切的标准中间件

ResearchKit:面向科研人员的数据搜集与疾病研究

在 HealthKit 打下数据互通的基本上,进一步的就是应用层面的问题了。有了这些健康数据,我们可以怎么使用它们?ResearchKit 是苹果最先想到的一个大规模应用场景。

在谈 ResearchKit 之前,或许你有必要了解一下临床科研项目的一些基本背景。事实上,世界上目前有许多罕见病和慢性病,我们并没有找到其发病的根本原因,对发病的机理也一无所知,甚至有治疗手段的有效性,也有待考证。没错,临床科研高度依赖试验,只有通过大量的患者入组参与临床科研,并通过有效的实验设计,收集大量的数据反馈,才能最终验证治疗方案的有效性和可靠性。

researchkit

然而,在现实情况里,有许多非常现实的问题:上哪找到足够多的愿意入组的患者?患者入组后,如何高效合理地开展试验?要知道,临床科研的合规性要求非常之高,哪怕只是错过一次治疗或者数据反馈,这一组「样本」可能就失去可靠性了。所以,临床科研项目往往面临着找患者难、找到患者之后实施更难的窘境。一次有效的临床科研项目,往往需要投入巨大的时间成本,耗费大量的财物跟踪患者的治疗和数据收集,而我们谈论的,可能只是几百个患者样本的试验而已。

ResearchKit 就是希望帮助临床科研人员解决这个问题。患者可以通过 iPhone 随时参与某一项罕见病或慢性病的科研项目,期间只要定期完成一些数据反馈和问卷调查,科研人员手中就能收获大量的数据。整个科研项目的流程都被简化了,现在患者只需要下载一个 App,首次进入时通过电子签名的方式确认参与项目并同意数据的匿名分享,之后定时打开 App,不管是输入自己的指标,还是回答问卷,或者完成一些小的测试,都可以随时随地在手机上完成。

apple-researchkit-1104340-TwoByOne

CareKit:以患者为中心的医疗干预和随访

ResearchKit 只是数据应用的一个案例,显然苹果并不满足于此。在科研人员之外,去年推出的 CareKit 则是面向患者个体的健康服务框架。和科研数据收集的导向不同,CareKit 的整体是以患者为中心的。通过 CareKit,患者可以随时与自己的医生共享自己的健康数据,并一对一地收到医生的反馈建议和治疗方案的调整,从而实现远程的医疗干预和随访。

Slice_1

不要小看了医疗干预和随访这件事情。虽然现代医疗越来越强调以患者为中心这个概念,然而很多并没有落到实处。许多人在看完《北京遇上西雅图》之后会开玩笑地说,中国医生的水平比国外医生高多了,因为中国医生一天看的患者数量,可能是国外医生一年的患者量。这句话其实并不全对,看病并不仅仅只是五分钟的面诊,而是一个动态调整的过程。实际上门诊和医生见面,仅仅只是治疗的开始,医生在通过面对面接触,得到化验结果的前提下,给你做出的治疗方案和开具的处方药物,并不是一个结果,而是需要后续根据病情的发展和治疗的效果,随时做出动态的调整。

对于医生来说,如果患者做完手术后出院,再也不和医生联系了,其实医生也并不清楚患者的术后情况和治疗效果,由于缺少信息的正向反馈,对自身临床水平的提高也不利。患者端则更好理解,如果医生能够持续地跟进治疗,患者的依从性和健康状况,也会有更好的改善。然而,我国由于医患数量的严重不对等,导致很多医生没有时间和精力来做好随访这件事情,而很多医院本身也不重视随访工作,或者工作方式还非常原始——不要怀疑,很多医院甚至还靠向患者写信,来收集患者出院的康复情况。

那么 CareKit 想做的事情就很好理解了,和 ResearchKit 的核心一致,本质上都是简化数据流通的过程,现在患者可以通过手机将健康数据实时共享给自己的医生,从而收获医生的建议和反馈,动态地调整治疗方案,对于医患双方来说,都大有禆益

ResearchKit 已取得的成果

前面提到了 ResearchKit 是面向科研人员的大数据收集工具,也是苹果在健康数据应用层面的第一次尝试,自从前年推出以来也有两年的时间了,这两年里成果如何呢?

Research-Kit-Apps-640x360

mPower 为例,它是由罗彻斯特大学、赛智生物网络共同推出的一款 App,主要研究的疾病是帕金森症,评估患者的情况并不难,只需要一些简单的小测试,让患者动动手脚,回答一些简单的问题,就能了解治疗对患者病情的改善程度。目前已经有超过 10000 人入组参与,其中 93% 的人从来没有参与过任何临床科研项目,不要小看了这一万人,这已经是帕金森症的临床研究中,有史以来规模最大的一次。在传统的研究中,一次试验能有几百个入组患者,已经是相当不错的成果了。通过 iPhone 的陀螺仪等功能,mPower 收集了患者一系列的数据,包括灵巧性、平衡能力、走路姿态和记忆水平,从而分析睡眠、运动和心情对疾病的正负面影响。

不仅如此,iPhone 的参与,还让许多临床项目融入了科技的元素。举例来说,小儿自闭症一直是另人困扰的问题,而许多家长往往在小孩已经好几岁时才发现,这时候治疗已经有些迟了。事实上已经有大量的研究证明表明,如果能尽早介入对自闭症儿童的干预治疗,儿童在成年后会有更好的智商和社交能力。Autism & Beyond 就可以使用 iPhone 的前置摄像头,通过对人脸的智能识别,甚至可以识别 18 个月大的婴幼儿的情绪反应,从而尽可能早地筛选出小儿自闭症患者,并进行干预治疗。利用 ResearchKit,App 仅仅推出一个月的时间,就比这个项目之前九个月通过各种渠道募集到的入组实验儿童都要多

Slice_2

苹果一度说的 There is an app for that,现在对于各类罕见病和慢性病来说,都是 There is an app for the disease。从之前提到的帕金森症和小儿自闭症,到癫痫、哮喘、脑震荡、慢性阻塞性肺病、糖尿病、乙肝、黑色素瘤、产后抑郁、睡眠健康等等,你都可以找到一款 App,主动共享你的健康数据,从而帮助临床科研人员更好地推动疾病的治疗发展。

这些疾病或许对你我来说并不陌生,但在科研人员眼里,还有太多他们想了解的谜团没有解决,例如产后抑郁和基因到底有没有关联,脑震荡长期来看对生活质量的影响,哮喘的个体化治疗方案等等,我们贡献的可能只是一点点匿名的数据,但所有人加起来,科研人员一下子能得到的数量据是以前的几十倍,甚至在糖尿病方面,几乎已经确认存在着数类 II 型糖尿病的亚型,而运动对这些亚型的治疗有着显著的影响。

而最棒的是,不仅仅只是那些患者,我们每一个普通人,都可以参与到推动医疗健康发展的进程中来。即使你一切健康,也可以下载诸如 mPower 这样的 App,并以一名健康者的身份共享自己的数据,从而帮助科研人员获取健康情况下的对照组

Researchkit__1_

CareKit 能做什么

前面提到了,CareKit 是在数据应用层面上,面向患者个体和医疗服务者之间的通道,是一套以患者为中心的医疗干预和随访。通过 CareKit,你可以随时与自己的医生分享自己的健康数据,医生在远程能够实时地给出建议和治疗方案调整。实际上,它主要有四个模块:

  • 健康卡:帮助患者执行和监控自己的治疗方案,例如通知你吃药的时间和剂量,该做什么理疗和运动,一些相关的运动数据还可以直接通过 Apple Watch 或者 iPhone 的传感器收集后直接上传给你的医生;
  • 症状和测量追踪器:患者可以记录自己不同时间段的情况,如睡眠质量、血糖血压情况,或者可以定时填写打分量表评估自己的心情或是疼痛水平,或者拍照记录伤口的愈合情况等等;
  • 透视仪表盘:通过图表的形式,直观地展示不同治疗方案的有效性和数据变动情况,以让患者直接地了解该疗程的效果是否达到;
  • 连接模块:患者可以直接与自己的医生在线沟通、共享数据。

image1458596369787

CareKit 以患者为中心正体现在:通过外界力量督促患者执行治疗方案、以患者可以理解的方式呈现数据和进展、针对患者的个体情况提供个性化的指导和建议、对接医生实时提供干预和调整。从前,你很可能需要随身携带笨重的设备全天候地记录数据,然后定期地回到医院、找到医生,现在,你只需要一部手机,就可以随时记录情况、分享数据、联系医生。

一个可以肯定的事实是,CareKit 相比 ResearchKit,会更快更大范围地普及,除了原有的一些 ResearchKit 项目会推出相对应的面向个人的基于 CareKit 的医疗服务外,德州休斯顿医学中心也正在研发一款基于 CareKit 的医患沟通 App;贝斯以色列女执事医疗中心则会面向慢性病患者,推出一套基于 CareKit 的家庭监控方案;One Drop 则正在开发一款糖尿病管理工具,不仅可以记录在不同血糖情况下的疼痛、晕眩和饥饿感,还可以实时地将这些数据共享和你的医生和家人。

苹果在健康产业的下一步

看到这里,你应该已经有了一个大概的认识:HealthKit 是一切的基石,它负责打通数据层面的对接和互通,ResearchKit 和 CareKit 则是两个数据应用层面的具体场景和案例,前者面向科研人员的大数据收集和分析,后者则是面向患者个人的健康管理和个体化治疗

那么,苹果在健康产业的下一步会是什么?

从目前来看,苹果的整个切入点还是在数据层面,的确在医疗体系里,数据标准化是一个很复杂的话题,即使是苹果目前做的,也仅仅只是部分最简单的数据的标准化,如化验结果、生理指标等等,但是苹果显然不会止步于此,毕竟要想在更多健康应用的场景里扩展可能性,就需要接入和统一更多的数据,包括患者的用药情况、手术情况、基因分子情况等等,这些数据的标准和结构化则更加棘手和复杂,显然苹果也在谨慎地调研。

此外,在数据层面来看,除了扩展数据的丰富程度之外,健康数据的安全性也是一个不容忽视的话题。如果我们的手机上存储了我们每个人最为重要的健康指标和数据,这些数据的隐私和保密就显得尤为重要。

在数据标准化的前提下,苹果当然是希望接入更多的健康场景和应用领域。未来,一定会有更多的可穿戴设备和医疗器械和 HealthKit 对接,随着检测仪器的小型化和便携化,未来化验和检查中心,甚至也可能完全消失,人们直接通过便携式的设备,即可通过标准的 HealthKit 协议互通数据,足不出户也能完全数据的采集。而这些数据,除了目前的临床科研和随访之外,在区域卫生预警、持续化健康管理、个体化治疗方案等等领域内,都有着广阔的前景和想像空间。

从某种程度上,健康领域的这些野心,体现的也是苹果所特有的一种文化:即希望赋予普通人以工具和能力,以积小成多的方式,发挥出协作和团体的力量。它或许没有神秘的实验室,没有令人眼前一亮的未来感,也没有太多的精英式的先导者,但正是凭借着这种文化和理念,苹果在设备和硬件之外,如此变革了多媒体内容产业、软件生态产业和教育产业,在不远的未来,医疗健康产业或许亦如是。

0

工程师成长的必备技能

经常听到有些工作1、2年的工程师说,觉得很迷茫,不知道该学些什么,想让我给一些建议。所以我总结了几个我认为对工程师很重要,越早学会越有用的知识或者技能。不知道学什么的时候,学这些总是没错的:)

基础知识

不管你是哪个方向,前端、后端、运维、QA、数据甚至AI等,基础的重要性再怎么强调都不为过。原因很简单,因为基础决定了一个工程师未来能达到的高度

就像建高楼一样,越高的楼,地基肯定需要打得越深。有些人会觉得平常工作里用不到那些知识,于是忽略了。他们按部就班的做一些日常工作,可能也没什么问题。但是当他们想要进一步提升自己的时候,就会发现好像有个天花板,怎么也突破不了。如果意识不到这一点,那可能一辈子就是一个平庸的工程师了。所以说,基础知识就是一个工程师的内功。内功不行,修炼再多的招式,也不可能成为高手。基础也会影响一个人分析问题解决问题的能力。基础不好的人,遇到问题可能都不知道去搜索什么关键词。

比如说性能优化,如果要做到极致,那就需要对硬件(如CPU、缓存结构、内存性能等)、系统(如进程、线程、内存分配、系统调用等)、网络(如TCP重传、拥塞控制、IP路由等)、数据结构、编程语言等都非常了解。再比如说 AI,数学和算法不好也能用 AI 做一些东西,但真的要训练一个好用的模型,基本的数学和算法知识就必不可少了。再比如系统分析和设计,编程功底不足、逻辑能力和抽象能力锻炼不够的人,设计出来的系统恐怕也是惨不忍睹的。

所以我建议大家可以把大学里对应专业的基础课程,找一些更好更深入的教材再好好的看看。比如对开发来讲,程序设计、数据结构和算法、操作系统、数据库、计算机网络、软件工程等等都应该看看。这些方面有很多经典的书可以参考,比如《算法导论》、《TCP/IP详解》、《深入理解计算机系统》、《计算机程序的构造和解释》、《代码大全》等等。如果有志于数据分析、数据挖掘、AI 方向的,数学、统计分析等也应该看看。我刚毕业那几年也算看了一些,但现在常常觉得还是不够,又没有时间再去重新学习基础内容。所以说,趁年轻有时间的时候,一定要多投入一些时间把基础打扎实了。

学习基础的时候,也不要死抠细节。更重要的是理解它的原理,学习解决问题的思路。比如,学习数据结构里的树,不是要你把这些它们的翻转、遍历算法倒背如流,而是要理解他们的原理、优缺点,知道在哪些情况下适用或者不适用,为什么数据库用B+树等等。当然,学习的时候动手写写这些算法,还是有助于理解的。

不过学习基础知识是很枯燥的事情,短期内也不会有明显效果,所以更需要耐心和坚持。基础扎实了,接下来根据发展方向的不同,就可以自己选择性的去学习了。

时间管理

这是我希望当年我刚毕业就学会的技能,可惜过了好几年我才明白它的重要性。很多人不会管理自己的时间和精力,白白浪费了不少大好时光。

时间管理最有用的我认为就是《高效能人士的七个习惯》里提到的“要事第一”原则,它的核心就是四象限。顺便提一下,史蒂芬·柯维的这本书,很值得一看,我看过好几遍,获益良多。

四象限.001

第四象限,不重要不紧急。 比如上网、游戏、购物、聊天、看朋友圈等。这些事情偶尔作为生活调节没有问题,但切记不可沉溺。有些人总抱怨自己没有时间学习,其实可以看一下,自己是不是花了太多时间在这一象限的事情上。

第三象限,紧急不重要。 比如不速之客、不重要的会议、日常的琐碎工作。对这些事,先考虑是否可以拒绝?是否值得自动化?或者是否有其他方式优化流程?如果都不是,那就尽快做掉。

第二象限,重要不紧急。 比如自动化或者改进效率的事情、工作规划、回顾总结、学习分享等等。这是最重要的象限(注意,最重要的不是第一象限),我们应该把大部分时间花在这里。这里的事情,大部分是着眼于未来,可以提升自己能力、改进工作效率的事情,但往往由于不是那么急迫,会被人忽视。

第一象限,紧急且重要。 比如线上故障、Deadline 等。这些都是要立即处理的事情。但其实这里的很多内容是从第二象限转变过来的,例如本来一个任务不紧急,但一拖再拖就变成紧急任务了。又比如如果我们把监控、稳定性做好的话,那线上故障就会减少。所以对待这个象限的原则是,尽量想办法减少这个象限的内容。

每次遇到事情,考虑一下它是哪个象限的,再决定如何处理。总之它给我们不少启示:

  • 把自己的时间花在重要不紧急的事情(第二象限)上。
  • 学会取舍。有的事情不一定要做,或者不一定现在要做。
  • 学会协作。有的事情不一定要自己做,可以找人一起帮忙。
  • 不要拖延。拖延会把不紧急的事情变成紧急的事情。
  • 不要钻牛角尖,把时间花在没有结果的事情上。

除了四象限,常用的时间管理方法还有 GTD 和番茄工作法。这些大家都应该了解下,但是也不用生搬硬套,而是要最终建立一套自己的工作方法。可以借助一些工具(比如我用的 Doit.im),学会规划好自己每天、每周以及更长期的任务。

项目管理

项目管理是一个很大的话题,很多人觉得项目经理才需要学这个,但其实对于初级工程师,了解一些项目管理的原则和方法,是很有帮助的。

比如说任务分解,看上去是很简单的事情,但要做好并不容易。任务分解的一个基本原则是 MECE 原则(相互独立、完全穷尽),也就是任务之间不能有重叠,并且不能遗漏。对于开发任务,按照垂直的功能去划分任务(即分成一个个完全独立的小功能),按照水平的分层去划分任务(即分成前端、服务层、数据层等),会有很不相同的效果。但怎么分解好,也是要具体问题具体分析,不能一概而论。

其实我们做任何事情,都需要在不同维度上进行任务分解。初级工程师应该要知道如何去分解自己的工作。比如以前我还用 C 和 C++ 的时候,见过不少人喜欢一下子先把代码都写完,然后再编译和调试。结果一编译,出现无数个编译错误,花费大量的时间修改和调试。现在 IDE 比较先进,这种情况不多见了,但很多人还是习惯先写好很多代码,再去调试。常常调试、修BUG的时间比写代码的时间还多很多。但其实我们可以把任务继续进行分解,例如 2 天的任务,我们可以把它分解成更小的模块、类甚至方法,每次实现了一部分,就去测试一下,或者写个单元测试确保这部分代码正确了,再去做下一个小任务。TDD 也是这种理念,不过它把写单元测试放到前面去了。并且认真规划的话,大部分情况都可以确保完成每个小任务后,应用功能还是可用的,随时可以提交代码。而不是要花上一个礼拜,等整个功能全部完成才提交代码。

再比如风险管理。有风险意识的工程师,会在开始任务前,仔细考虑可能遇到的各种问题,采取一定预防措施,做好沟通工作,并考虑好对策。而不是等到问题出现就手足无措,很多延迟和返工就是因为这类原因出现的。

为什么优秀工程师效率会比一般工程师高很多?原因之一就是优秀工程师会用项目管理的思维去规划他们工作,采用最合适的方式去完成任务。所以初级工程师应该了解一些项目管理知识,包括WBS、MECE 原则、各种估算方法、风险管理等。这些对自己当前的工作也好,对未来承担更重要的职责也好,都有很大的帮助。

持续学习

软件行业的知识和技术日新月异,作为一个软件工程师,要不想自己被淘汰,就只能不停的学习。

十年前我刚毕业的时候,网上的资料很少,开源代码更少,要学东西只能自己去买书然后边看边实践。现在不一样了,只要不是太冷门的内容,都能在网上找到各种教程、文章甚至还有开源代码,学起来方便多了。

对于工程师来讲,学习需要既有一定的广度,同时也要在某个特定方向有足够的深度。广度方面,可以定期学习一些新的知识,关注行业的最新动向。至于深度,那还是要去看书、看代码、动手实践。对于开发工程师,可以多阅读一些优秀开源项目的代码,也可以上 Leetcode,Topcoder 之类网站刷刷题,对自己的功力提升有非常大的帮助。

然后就是尝试多写、多分享。一个知识,当我们尝试要去把它写成文章的时候,常常会发现我们对它还有很多不了解的地方。特别是要用通俗易懂的方式把它写下来,其实非常难,需要我们对这个知识理解的很深刻。这也是我们杏仁要求每个工程师都去写知识库,并且鼓励大家做分享的原因。前面的一篇文章,《思维阅读法》也提到了分享的重要性。

除了自己专业的知识,大家也不妨多了解些其他专业和行业,比如经济、历史、财务、运营等方面的内容。学习多种思维模型,避免手里只有一把锤子,然后把所有问题都看成是钉子。

锻炼身体

最后再加一个彩蛋。很多工程师都有颈椎、腰椎的问题,很是痛苦。我也是,有段时间脖子疼腰疼,特别不好受。以前虽然也运动或跑步,但很不规律。最近几年开始健身跑步,一周保证至少两次,颈椎和腰痛都好了很多。平常注意坐姿,劳逸结合,坚持锻炼,不仅可以免受很多痛苦,精力也会更充沛。


当然想要成长为大牛,光靠这里说的这些还是不够的,还有很多东西需要不断的学习和磨练,比如沟通协作、系统设计、产品思维等等。但我认为上面几点,是最核心最基础的,可以说是“元”技能,越早学会这些“元”技能,能够更好学习其他知识和技能,更快突破自己。

0

四维阅读法 – 我的高效学习“秘技”

引言

我常常不自觉地把学习、工作中遇到的一个技术点钻成了无底洞,通过互联网一下子能获取无穷的信息和书籍,查询一个关键词的时候会遇到更多相关的关键词、畅游知识的海洋无比满足,同时也如深处泥淖一般无法自拔、常常迷失了目标而走了不必要的弯路;于是在最近的学习过程中整理出这套实践方法,作为阅读的指导以防迷路走失。

日常阅读时常常在想:有没有更好的方法和工具来帮助自己更有效、更高效、更系统地去阅读和做笔记?也一直在搜寻各种效率工具和APP,但始终没有找到某个能满足我所有需求的工具:思维导图不方便深入讨论细节、Markdown不方便构建层次结构、UML图不方便书写文档、Code能容纳的信息就更少。最后得出结论:单单从某个维度去穿透阅读无法达到系统学习的目的,必须综合运用多个维度的方法和工具。于是就有了:四维阅读法。

四维阅读法包含四个维度,分别是:

  • 广度 Breadth,使用思维导图工具输出成果,如 MindeNode
  • 深度 Depth,使用文档工具输出成果,如 Markdown
  • 形象 Imagine,使用图表工具输出成果,如 UML
  • 实践 Practice,使用项目和代码来输出成果、并分享自己学到的知识

四个维度的方法和使用务必坚持分治思想和职责单一的原则,每个维度只做自己领域内擅长的事情,最终组织成系统的知识结构体系。下面将从四个投射问题引出四个维度的解决方案。

第一维度,广度穿透

拖延症与行动力的问题

不只是阅读,现在生活遇到的难题是常常拖延,以至于到最后的dead-line时刻才匆匆忙忙去完成一件事情,不仅精神压力大事情完成得也不够理想,这是问题的症结所在。

读吴伯凡《拖延症,为什么成了经久不衰的话题?》后受到启发:拖延症是一个伪命题和借口--拖延症是你不想做某一件事情的借口,如果你真的想做某件事你根本就不会拖延而是想尽办法去达成目标,所以需要治疗的不是拖延症,而是去直面你需要去解决和完成的问题和事情。从医学角度来解释一下“拖延症”为什么如此难以战胜:不想做的事情会触发大脑神经的痛感中心,让你感觉痛苦、难熬,而我们去找寻所谓解决“拖延症”的解决方案、想一劳永逸地解决“拖延症”的问题,去读各种分析“拖延症”的文章和鸡汤,心灵得到了慰藉,痛感消失,最后便对“拖延症”不了了之,之后当你遇到另一件不想做的事情的时候,“拖延症”又复发了,于是又去寻找解药走上了老路,如此循环无解。所以明白了吗?“拖延症”的解药是一种“安全无效药”,本质上不过是一种止痛药罢了。人生中会遇到无数你不喜欢不想做但是需要你去做的事,这个时候会本能的逃避和拖延,但要做事就必须面对、要做事就必须有勇气和决心。别无它法。

广度穿透

广度穿透的目标是:构建知识的层次结构、思路,输出思维地图、任务清单,并帮助解决拖延症和行动力的问题。所以这个侧重点和主要职责是--分类,而不涉及具体、细节问题的描述或者讨论。把这份地图当成必达使命去执行和完成,最终完成系统阅读和学习。

思维地图是解决方案、也是算法,它让自己能够随时意识到自己所在的位置和上下文,它是一张准确精细的地图,给自己的行动提供方向;它是撒出去的网,给自己的决策提供收放依据。

工具

思维导图工具 MindNode。

示例--读《Java并发编程实战》:

breadth

第二维度,深度穿透

虚无的知识网

通过第一步广度优先地梳理,已建立完整的知识网,第二步需要深入细节的描述和讨论来详实和支撑知识网。

深度穿透

深度穿透,是通过逻辑力和表达力来穿透概念和知识点。描述清楚是基本要求,逻辑严谨是深度要求。

工具

Markdown书写工具。

示例--读“Akka官方文档”笔记:

depth

第三维度,形象穿透

混沌的知识网

一个需要用千言万语才能描述和解释清楚的问题,往往用一张图表就能完美清楚地说明。语言使用的是逻辑力量,而图标使用的是想象力:将逻辑形象化。

一个复杂的问题即使能够用语言描述清楚,在阅读者的大脑中仍旧是一些馄饨、抽象的概念。阅读者的想象力与书写者的表达力是正相关的,表达力不足,想象力和理解力也将受限。所以,需要同时调动表达力和想象力来相互助益。

形象穿透

形象穿透,是将复杂、抽象概念形象化、具体化的过程,帮助理解和记忆概念和知识。简单具体是形象穿透的第一原则。在形象化过程中可以使用类比,比如并发问题可以类比成“一团杂乱的线团”,它和并发问题一样让人心烦意乱而且无从下手,所以在描述清楚并发问题后配上这样一张图片对阅读者会有很大的启发作用。

这一维度的灵感起源于:Google图片搜索学习法。遇到一下子无法理解的概念,Google搜索关键字也无法找到满意的解答,这时切换到Google图片搜索,往往能得到一目了然的图解,顺着图片查看出处网页就能找到更优秀的资源。

形象穿透常常有两种方案:一是网上搜图片、二是自己动手画图。面对复杂陌生的概念和知识,即使能找到完美的图解,仍然建议自己动手画一画,这样能够帮助深度理解和记忆。

工具

图表工具 draw.io。

示例--“Java的锁”图解:

imagine

第四维度,实践穿透

纸上得来终觉浅,绝知此事要躬行

阅读得来的知识,在消化和透彻理解之后,内心会有非常强烈的满足和快感,这是“引诱”我们继续深入阅读的“糖衣炮弹”,要十分警惕这种感觉。把它当成一个信号:一个警醒自己沉淀下来、继续虚心前行的信号。否则会沉沦于快乐的舒适区中,忘记自己学习的初衷和目标,飘飘然而遗忘自己所在的位置。所以,问题的关键在于,如何沉淀下来?如何将读到的知识变成自己的知识(或者说思想)?

古人云:读万卷书行万里路。在获得足够的知识之后,应该马上到实践中去应用、验证你所得到的知识。于是知识就变成了你人生中实实在在的经验和阅历,而不再是虚无抽象的概念了。

那么,要如何去实践呢?

实践穿透

个人总结下来有三种非常有效的路径:

1. 运用到工作中去

如果你学习的知识正好工作上能够用到,那就再好不过了。以工作为战场去应用和检验自己学到的新技能,既巩固了知识、更使所学产生价值,实在是一举多得。

2. 贡献开源代码

工作中有施展空间当然是非常幸运的,然而更多时候,自己的学习和研究是无法马上应用到实际工作中的:商用的前提是稳定,应用新技术和方案带来的变化,其影响和风险是很难量化和估计的,所以往往条件不成熟。这种情况,就要另辟蹊径,在工作之外寻找空间去实践。

我想,再没有比开源社区更好的第二空间了吧,在社区能去做自己感兴趣的项目,能为开源项目贡献力量,更能遇到志同道合的伙伴;我相信,有团队的帮助,在学习的路上你将走得更远。

3. 去分享

大家可能往往觉得实践就只是去应用知识,其实不然,实践还有一个非常重要的方面,就是去分享知识。去分享你就会发现:原来我并没有自己想象中那么理解这个概念?原来要将知识解释清楚这么难?原来并不是只有我一个感兴趣这个领域?原来这个概念还能这么理解?......分享的过程,你能更深刻地理解知识,能找到知己,能锻炼自己的表达。

去分享,你也可以成为一个布道者。

工具

能帮助你更好地去完成实践的工具就是一些方法层面的实用工具了。一是使用项目管理的方法,推荐阅读《一页纸项目管理》;二是懂得取舍和决策,推荐阅读《简约之美--软件设计之道》。

总结

从算法的角度来总结一下四维阅读就是:

  1. 广度优先建立学习的地图
  2. 在学习地图上有取舍地深度阅读
  3. 根据前两步的成果运用形象表达加深理解
  4. 去实践
  5. 迭代学习:实践中遇到问题?如果是方向错误,则回到第1步修正学习地图;如果是知识概念没有理解或理解错误,则回到第2步继续深度阅读

阅读中往往很容易就忘记了阅读的目的,阅读,最终还是为了解决实际问题、内心的疑惑。但阅读过程中会遇到目标之外的海量信息、知识和更多的疑惑,阅读的航道很容易偏离目标,导致目标的拖延。所以阅读的过程中,需要不断的确认和修正自己的航道。理性阅读,需要更多的克制和坚持。请明确目标,理性决策,正确前进。

0