目录
本书以软件工程师出身的创业者的角度,全面介绍了公司该如何打造产品、实现技术和建立团队,既是为创业者打造的一份实用入门指南,又适合所有程序员系统认识IT行业。书中内容分为三部分——技术、产品和团队,详细描绘创业的原始景象,具体内容包括:创业点子、产品设计、数据与营销、技术栈的选择、整洁的代码、软件交付、创业文化、招兵买马,等等。 本书适合所有程序员。
那是因为创造力的产生可以归结为三个阶段,这三个阶段都不过是不同形式的重新合成:
当我们学习一种新的创造性活动时,模仿总是要做的第一件事。婴儿通过模仿成人来学习,艺术家通过模仿大师来学习,程序员则通过复制粘贴来学习。转换类似于模仿,但对原有的点子做了一些改进,就像爱迪生为电灯泡研发新的灯丝一样。合并意味着要把一些已有的点子组合成比各部分都要好的一个整体。例如,古登堡并没有发明螺旋压力机、活字、墨水和纸张,但他能够把这些东西凑在一起做成印刷机,得出一个和每个组成部分都大不相同的新东西。模仿、转换与合并已经深深地根植于每一个生物体当中。事实上,这也是我们之所以成为生物体的过程:我们的细胞进行自我模仿(有丝分裂)、转换(由随机突变引起)和合并(当你被繁殖而成的时候)。从非常实际的意义上看,你就是父母及所有祖先的混合。当然,这也不意味着盲目地偷窃别人的成果就可以想出新的点子,而是说创新的最佳方式是研究、注明出处、重新合成、聚合和转换。所以,如果我们想要找到创业的好点子,就需要一整堆的“原料”,从而可以在此基础上去研究、注明出处、重新合成、聚合和转换,也就是说,我们需要掌握大量的知识。
如果知识就像利滚利,那么越早投资越好。从今天开始,想尽一切方法开始学习,哪怕只是一小点,其效果也会超出你的预期。但是,我们应该花时间去学习什么呢?最有效的一个策略就是努力成为一名T型人。既是通才(在许多有价值的方面高度熟练——T的横),也是专才(在某个特定的学科中属于领域内最出色的——T的竖)。
只要你能很好地把知识的深度和广度结合起来,就可以把知识转变为点子。
每当我们学习一些新东西的时候,都是一种有意识活动的开始,经过足够的练习之后,就会成为一种下意识的活动。
精益和敏捷方法的核心原则就是尽可能快地把可用的产品放在用户面前,即便产品离最终完成还有很远的距离。相反,瀑布方法则是希望先做出完整的解决方案,再呈现给用户。在理想情况下,从长期来看,用这两种方法去实现功能齐全、成熟的产品所需要的时间大体上是差不多的。但在实际中,多数项目在“概念验证”阶段就止步不前了。
在这一过程中,用敏捷和精益方法会更早地发现错误;反之,如果使用瀑布方法,就要做出一个完整的产品,才可能意识到它是错误的——这将花费大量的时间和金钱。假如你对一个产品已经做了大量的投入,编写了许多代码,把它抛弃会很困难,也会让你更加泄气。但是,你又不得不把它丢掉,重新回到绘图板前。不仅如此,你可能会多次经历这样的过程。其中展示了不同方法发现错误(普遍的说法是获得真实用户的反馈)所需时间的差异,这正是速度制胜这句话的意义所在。
博伊德法则:迭代的速度会打败迭代的质量。
无论是博伊德法则、速度制胜、敏捷还是精益,它们背后的基本理念都是:我们有一些设想是错误的。问题是,我们不知道哪些是错误的。也许你做的是错误的东西,也许你把东西推销给了错误的受众,也许你的商业模型是错误的。要做一个成功的产品,就必须分辨出错在什么地方,然后解决问题。在创业领域,一切都在快速变化,唯有连续不断地重复“发现问题-解决问题”这样一个过程。
在顾客看来,界面就是产品。
对于顾客而言,产品的设计就是他们所在意的一切。这种情况叫作“冰山的秘密”。我们所看到的冰山在水面上的那部分只占它总体积的10%;同样,我们可以看到和触碰到的产品的那部分——用户界面,只占全部工作的10%。所以,这个秘密就是大多数人并不清楚这一点。当人们看到很糟糕的用户界面时,他们会认为该产品的一切都很糟糕。如果你要向潜在客户进行演示,你就得精心雕琢你的展示结果,这才是最重要的。你不能让客户去想象产品将来是怎么样的,现在只要关注“功能”就行了。如果界面的像素看起来很糟糕,人们会认为产品可能也是很糟糕的。
大多数人错误地认为设计就是东西看上去的那个样子。人们认为设计就是外观,比如设计师接到一个盒子,被告知把它弄好看。这不是我们所认为的设计。设计不仅仅是看上去的样子,还关乎它如何使用。——史蒂夫·乔布斯
设计关乎产品如何使用。iPhone比其他大多数智能手机都更加漂亮,这为它增色不少,但iPhone的出色之处不仅在于它的款式。它清晰的屏幕、排版和布局使文本变得更容易阅读;它的按钮很大,易于使用;它的触摸屏非常精确,UI又快、反应又灵敏。iPhone可以预测你的需求,根据周围环境的光线自动调整屏幕亮度,或者在你拿着手机靠近耳边时将屏幕完全关闭。你在使用中完全不需要考虑它,不需要跟它较劲,它就是好用。虽然其他智能手机也许在功能或价格上不比iPhone差,但它们仍然无法达到这种体验水准。这就是Apple如此看重设计的原因,自然而然,这也将Apple造就成了世界上最具价值的公司。
设计的目标是为了与用户沟通,这意味着虽然“看起来漂亮”是设计的一个很有价值的因素,但是更为重要的是要认识到设计是为了帮助人们实现他们的目标。因此,每一次设计都要从理解用户开始。
一个好的办法就是要认识到我们不能在工程或产品完工之后,才把“设计”加上去。设计就是产品,从产品开发的第一天起,它就应该是其中的一部分。以用户为中心的设计应该纳入我们的产品开发过程中,下面是它的五个基本原则:【1】用户故事;【2】人物角色;【3】情感设计;【4】简单;【1】可用性测试。
所谓用户故事,就是从用户的角度简短地描述你所做的东西。它应该回答下面三个问题。用户是谁?他们要实现什么?他们为什么需要?
如果你是一名程序员,想理解你的用户会更加困难。每个人都会对“一种产品如何工作”形成自己的概念模型。但程序员的模型通常是非常细节化的,一般都处于界面、事件、消息、API、网络协议和数据存储这样的层次,而典型的用户模型通常没有那么多的细节,既不精确也不完整(例如,许多用户是不能区分软件与硬件、显示器与电脑有什么差别的)。这种概念模型上的不匹配会导致程序员很难和用户沟通。
其中要把握的关键点是:沟通是设计目的之所在。我们努力把信息呈现给用户,告诉他们可以做什么,向他们展示如何去做。不幸的是,许多程序员并没有意识到,因为对自己的软件非常了解,所以考虑软件的方式和用户是完全不同的,我们全然记不起初学者面对我们的软件是什么感觉。这种情况称为知识之祸,是一种认知效应。
作为程序员,当你在设计软件的时候,你的大脑其实一直都在保持着自己的认知。然而,你的用户却什么都不知道,他们必须通过你所设计的用户界面(user interface, UI)去使用软件。你不能期待用户知道你所知道的,你也不能指望用户通过文档或教程来填补这一鸿沟。(正如Steve Krug所说的:“关于说明书你必须知道的最主要的一件事就是,没有人想读说明书。”)所以,想要做出成功产品的唯一选择就是做出出色的设计。
编程的过程和制作易用产品的过程是格格不入的,简单来说就是程序员的目标和用户的目标是有显著差别的。程序员希望构筑产品的过程顺利简单,用户则希望与程序的交互顺利简单。而这两个目标通常都不会共生于相同的程序。
你关注的目标越广泛,错失靶心的必然性就越大。想让大量人口中50%的人满意你的产品,从而实现50%产品满意度的目标,这种做法是行不通的。我们只能挑选出50%的人,想方设法让他们100%满意,才能实现我们的目标。我们甚至可以瞄准市场中10%的人,让他们100%地心醉神迷,从而取得更大的成功。这听起来可能有点违背我们的直观感觉,但为单个用户进行设计是满足广大人群需求最有效的方式。
只要有可能,我们就应该把软件设计得像把你记在心上、考虑周到的人一样。要记住用户的参数设置,记住他们上次使用你的软件做了什么事情,记住他们过去搜索了什么东西,要尝试使用这些信息预测用户在以后会做什么事情。例如,大多数网页浏览器都会记住你过去输入的网址。Google的Chrome浏览器甚至更进一步,只要你一输入www.goo,它不仅会替你把网址补充为Google首页,而且如果该网址是你之前已经多次输入的,它还会在你点击回车之前就开始抓取网页,让网页加载得更快。Google对于密码的考虑也很周到,如果你最近修改过密码,而不小心还用老密码去登录,Google会提醒说“你的密码已经在12天前修改过”,而不是给你标准的“密码无效”的错误消息。
人都会犯错,而且还会不断犯错。在设计软件时,要假设用户也会输入错误、点击错误的按钮或者忘了一些重要的信息。例如,在Gmail中发送email时,它会扫描你写的文字,看看是否有“附件”这个词,如果你忘了附上一些东西,它就会弹出一个确认对话框,核实你是否是故意的。同样,在你点击了“发送”按钮之后,Gmail会留给你几秒钟的时间,让你可以“撤销”操作,以防你改变主意或者忘记了一些重要细节。我希望所有的软件都有“撤销”按钮。有时候,我希望生活也有撤销功能。
“对错误的发生要宽容”这一点真的太重要了;
没有人喜欢错误消息,没有人想看到“PC Load Letter”这样不知所云的提示,最重要的是,没有人想感到出错是因为他们犯错导致的。在线表单通常是最让人恼火的,你花了很长时间去填写几十个文本框,点击提交,页面一重新加载,就在页面上看到了一个很难理解的错误消息。有一些网站特别让人恼怒——你根本就不知道做错了什么,输入的所有数据就都不在了,这表明设计人员并没有彻底想清楚应用程序的错误状态。以下是一些经验法则,可以避免这种错误的发生。
【1】提供帮助和指引,而不是错误消息。例如避免使用“错误”“失败”“问题”“无效”和“异常”这样的词,而是向用户解释程序希望获得的输入与用户的输入之间有什么差异。
【2】在用户输入的同时进行检查(而不是在页面提交之后再进行),并分别给出肯定和否定两种反馈,给出的反馈应该在用户视线附近(而不是页面的顶部)。
【3】永远不要把用户做好的东西弄丢。
当你需要尽可能低成本、快速地向客户验证你遇到的每一个新问题和想法。最好的方法就是实现所谓的最简可行产品(minimum viable product),或者叫MVP。
MVP是“新产品的某一个版本,团队可以利用它以最小的付出去最大程度上、验证性地了解客户”。MVP的关键就是从中学习,其目的就是找到成本最低的方式去验证对真实客户的假设。
MVP中的“最简”意味着所有对当前假设的验证没有直接帮助的东西都应该被排除。MVP中的“可行”意味着MVP能够让客户接受它。MVP也许有bug,也许还缺少某些功能,也可能外观不怎么好看,甚至和最终实现的产品根本就不一样,但是它解决了客户所关心的问题。
上图展示了不可行的MVP和可行的MVP之间的差别。
产品应当关注的数字对每家公司来说都是不一样的,但是有几种类型的指标是所有公司都需要跟踪的:【1】获取(acquisition);【2】激活(activation);【3】留存(retention);【4】推荐(referral);【5】收益(revenue);【6】神奇数字(the magic number)。
【1】获取:我们应该关注的第一个指标是获取,或者说是用户如何找到你的产品。
【2】激活:在用户发现了产品之后,接下来要跟踪的就是激活这一指标,这是测量有多少用户被你的产品所吸引,进行了账号注册、邀请朋友、执行搜索或者支付等动作。
【3】留存:下一个阶段就是让激活的用户回来并再次使用你的产品。在某种程度上,这也是一种获取,但是用户的留存通常利用的是不同的渠道,所以应该分别进行跟踪。
【4】推荐:在某种意义上,这也是另一种形式的用户获取,但我们关注的是一个确定的渠道:在产品现有用户的帮助下获取新的用户;用户推荐指标的重要性不仅仅在于它是我们获得用户的一个来源,也是衡量产品质量的一个指标。除非你真的喜欢一个产品,否则是不会把它推荐给朋友的,所以用户推荐数的增长通常是衡量产品是否改善的一个好方法。
【5】收益:我们也应该跟踪一下我们到底赚了多少钱,这些钱是通过什么渠道获得的,比如销售、订购、广告、业务拓展。
【6】神奇数字:每一个公司都有一个“神奇数字”。这个指标就是,一旦用户突破了这个指标,他们就会遇到“惊喜”时刻,最终“粘”上产品。例如,对Facebook而言,表示新用户成为高度参与的用户先行指标就是“在他注册的10天内联系7个朋友”的神奇数字;对于Twitter,一名新用户只要关注了30人之后就很可能成为一名活跃用户;在Slack,只要一个团队交换了2000条消息,他们中的93%就会一直成为Slack的用户。
数据驱动开发有许许多多的内容,但我们最常用的就是A/B测试。
“酒香不怕巷子深”的说法并不正确,因为产品不具备自我销售的能力。想要获得成功,你不仅要做产品,还要找到营销方法。我们不妨把营销当作是产品设计最必不可少的一部分。如果你有了新发明,却没有销售它的有效方法,这就是糟糕的生意——不管你的产品多么出色。
一个比较推荐的方式就是先从小型、简单的技术栈开始,并创建一个在必要的时候能够改造技术栈的进程,使之适应来自环境中的新压力,比如需要处理增长的流量、新来的员工和新的功能。换句话说,我们更应该关注如何构建可以不断进化的技术栈,而不要太过关注现在什么是“最佳的”技术栈。
事实上,也不存在所谓“最佳的”技术栈。如果选择技术时没有考虑产品类型、团队和公司文化,就好比在买房子、做预算或者在弄清和谁一起住之前,就先决定了要买什么家具。所以,情景是至关重要的。
最初选择的技术并不是关键,最初的决定在最后看来一定是错的,唯一的问题是它会错多久。关键在于,你要在遇到拐点时有壮士断腕的勇气,而不是为了存活而一层一层地给它贴上创可贴。
了解何时改变你的技术栈从根本上说是可扩展性问题。当你违背了技术栈的黄金法则——当你发现人数的扩展快于技术的扩展——就是时候重新进行评估了。如果新功能的实现比你预料的时间还要长,每次发布新版本影响到的功能比新增功能还要多,也许就是时候去改变了。我们有时必须要置换技术栈中较大的部分,比如迁移到不同的数据库,但是要警惕使用停止一切、彻底重写的做法。
暂停所有的发展、在全新的技术栈上重写代码要冒巨大的风险。这种情况被称作“所有软件公司都可能犯的单一的、最糟糕的战略性错误”和“创业自杀”。如果你抛开旧代码,就等于抛开了多年的学习和修复的bug。在重写的时候,你终将重复面临许多相同的错误,还要加上许多新的错误。你会意识到,用全新的技术去重写代码只是问题的一小方面,你的大部分时间将花费在重新培训团队成员用新的方法去做事情,说服他们新方法比老方法好,还要更新文档,处理数据迁移的问题,将该技术整合到构建和部署系统中,设置监控,解决调试新技术的方法。代码的重写就是侯世达定律(Hofstadter's Law)的最佳例子:你做事所花费的时间总是比你预期的长,即便你已经考虑了侯世达定律。与此同时,你的产品正深陷泥潭,你的竞争者正在超越你。
那么,要如何让技术栈进化且不会扼杀你的创业呢?答案就是渐进主义。其思路就是把任务分解成小的、孤立的步骤,每一个任务都有它自身的价值。但并不是所有“小步骤”都是生而平等的,所以要警惕错误的渐进主义。
对于技术栈的每一部分,我们都要判断应该是内部去实现、购买商业产品,还是使用开源项目。
虽然编程语言的性能很重要,但对于大多数创业公司或者中小企业来说,程序员的性能才是更大的瓶颈。我们要寻找一门能够以最少时间去完成最多工作的语言。生产效率涉及两个主要方面:有多少现有的代码可以重用,以及你能够创建新代码的速度有多快。
现有代码的数量取决于这门语言的流行程度以及社区的规模。流行的语言拥有更多的学习资源,我们也可以雇到更多已经熟悉该门语言的人,可以使用更多的开源库。成熟语言的生产力工具也是一个生态系统,比如IDE、分析器、静态分析工具和构建系统。我们可以重用的代码越多,需要自己编写和维护的代码就越少。
创建新代码的速度取决于三个因素。第一个因素是经验,你对一门语言的经验越丰富,生产效率就越高,所以要寻找你和团队已经熟悉并且有丰富文档且易于学习的语言。第二个因素是反馈循环,就是代码修改后需要多长时间才能看到效果。如果必须等待几分钟才能完成代码编译和部署,与只需要等待几秒钟让页面刷新或让脚本返回相比,前者的生产效率更低。我们要寻找支持热重载(hot reload)、具有交互式编码环境(比如“读取-求值-输出”循环,REPL)、快速编译、快速自动化测试的语言。第三个因素是语言的表达能力,即对于任何给定的想法,需要多少行代码才能实现。需要编写和维护的代码行数越多,面对的bug就越多,前进的速度也越慢。一般来说,我们应该在满足其他需求的前提下,尽可能挑选最高级和简洁的语言。
如果你已经到了害怕修改代码的地步,就意味着你需要在编码实践上进行扩展,以适应增长的需要。应对不断增长的代码库和开发团队的最重要的四个编码实践是:
·自动化测试;·代码分离;·代码评审;·文档。
自动化测试具备了迭代式的代码测试循环的优势,这样我们每次修改仍在运行中的东西时可以充满信心,不必在脑海中记着整个程序的状态,不必担心弄坏其他人的代码,也不必一次又一次重复同样无聊、容易出错的手动测试。我们只需要运行一条测试命令,就能够快速得到正常与否的反馈。
如果整洁的代码就是让其他程序员可以更容易地理解你的代码,那么实现这一点的最好做法就是把代码展示给其他程序员看。让代码被其他人评审是捕捉bug最有效率的手段之一,一项对有着11名程序员的团队的研究表明,在他们开发出来的没有代码评审的程序中,平均每100行代码就有4.5个错误,而那些有代码评审的则平均每100行只有0.82个错误,错误率下降80%以上。另一项研究表明,虽然单元测试和集成测试均可以捕捉到大概30%~35%的bug,但设计评审和代码评审可以捕捉到55%~60%的bug。
除了捕捉bug之外,代码评审还有另一个重要的好处:它们是在整个团队间传播知识、文化、培训以及所有权感的一种高效机制。所有参与代码评审的人都会享受到这种好处:高级工程师可以利用代码评审去指导初级工程师,初级工程师可以通过参与代码评审去学习代码并贡献一些重要的问题。如果新的开发人员无法理解这些代码,也许是因为开发人员经验不足,但也有可能因为代码是混乱的。代码评审分为四种类型:设计评审、结对编程、提交前评审和静态分析。
代码评审是每一个公司都应该应用的必备实践,但要更好地实施也要遵循若干指南。
需要提前将代码评审指南写下来,营造良好的代码评审文化。这些指南应该包含评审者期望掌握的事项的备忘录,比如代码是否容易阅读、是否遵循团队的编码约定、是否包含测试,这些指南也应该定义实施代码评审的代码。
代码评审并不是炫耀自己的知识,取笑他人的代码或者责怪他人。它是一种学习的手段,能让团队中的所有人变得更加出色,让所有人都感觉(及表现得)像主人一样。为了让所有人都达成共识,代码评审指南应该定义什么代码必须被评审(例如是所有的代码还是实现关键任务的那部分),什么时候代码必须被评审(例如是每次提交前、合并分支前还是在每周的评审会议上),谁的代码必须被评审(例如所有人,不管级别有多高),谁负责给出评审意见(例如所有人,不管级别多低),什么类型的意见是合适的(例如花时间标注代码好的一面,而不仅仅是不好的一面;永远不要侮辱提交者本人;不要害怕承认你不理解某些内容)
文档对于代码规模的扩展和开发团队都是必不可少的,即便团队只有一个人也是如此。编写文档就像编写自动化测试,可以显著提升代码的质量。如果你强迫自己从用户的角度去看待项目,将会得到更好的设计。如果你花一个小时用文字去描述正确的解决方案,你就能节省下用代码编写错误解决方案所需的一个星期。
这里说的“文档”不仅指参考手册,还指所有帮助学会软件的东西,包括书面文档(Readme、教程)、代码文档(类型系统、注释)和社区文档(Q&A网站、邮件列表)。每种类型的文档都在解决不同的问题,所以大多数项目都应该混合包含各种类型的文档。
在《优秀到不能被忽视》一书中,Cal Newport证明了出类拔萃的表现不只需要大量的练习,还必须要刻意练习。刻意练习意味着要有反馈机制,让你可以跟踪表现水平,在每一段练习中,都要有意挑选一些在你能力之上的活动。刻意练习的经典例子就是举重,杰出的举重运动员不是在锻炼——他们是在训练。他们把杠铃的重量作为一种反馈机制,记录训练过程中的每一次表现;在每一次训练中,他们会尝试举起比上一次训练更重一点的重量(这一概念称为渐进式超负荷)。在创造性任务上对智力应用这种训练也是可行的,尽管你的反馈机制可能更难以测量。例如,披头士乐队的反馈机制是听众的反应,夜复一夜。程序员的反馈机制也有一些容易测量的方面,比如代码是否能够通过所有自动化测试,也存在一些更为主观的方面,比如在代码评审中从另一位程序员那里获得的反馈。
无论哪种方法,刻意练习都会提升你的能力,这种提升通常也会带来相当程度的不舒服感。刻意练习通常是“乐趣的对立面”,就像举重运动员已经习惯了体力耗尽的感觉,找出让他们的肌肉燃烧的活动区。你也需要习惯这种精神耗尽的感觉,找出让大脑承受压力的任务。累计数千小时刻意的、不舒服的练习是不容易的。为了达到1万小时,你需要每周大概练习20小时,一周接一周,持续10年。这需要有严格的时间保证,为此你需要做到:·明智地选择技能;·投入时间去学习;·让学习成为工作的一部分。