从创意到产品:ToBuy的故事

一个故事:从想法到产品 毫无疑问,新冠疫情在很多方面都深刻的影响了我们的生活。不论是出门必须戴口罩,在户外保持社交距离,还是外出回来要使用6步洗手法洗手,又或者由于远程办公而形成的物理隔离带来的心理上的负面影响等等,这些都深远的影响了我们的日常。 我今天要分享的一个故事正是与此相关:我是如何在维州宣布进入灾难状态的一个多月后,不得不全天几乎24小时呆在家里的情况下,从零开始进行一个iOS App开发并最终上线的故事。而这一切自然要从原始的需求分析开始说起。 原始需求分析 这个需求其实由来已久。在平时购物的时候,为了避免花费太多时间在超市,很多人都会随身携带一个购物清单,像下面这张图上列出来的那样: 大家平时买东西也可能有类似这样的纸质的购物清单吧。这种购物清单便于携带,随用随扔,非常方便。不过有几个小的缺点:一是买完这次之后,下次再去买的时候很可能卡片不知道丢到哪里去了;二是如果不配合笔一起使用的话,很可能要检查多次清单才能确定某样商品是否已经买过。此外,纸片上的信息毕竟有限,有时候你想要描述类似这样的需求:在永辉超市买甘竹牌豆豉鲮鱼,不要错买成鹰金钱,就会发现纸和笔可能不够用了。 我希望有一个手机App可以帮我管理这些清单。因为手机基本上是我出门必然会带的设备,手机本身可以作为纸笔Todo的替代品,而且有图片作为参考可以快速定位并找到我需要的商品。 另外一个典型的购物场景是:我每次购物往往会去多个超市,有的超市新鲜果蔬比较好,但是价格偏贵;而另一家则百货齐全,有时候水果不太新鲜;另外有一些商品比如豆腐乳,胡椒粉之类只能去亚洲超市才能买到。这时候我希望这个App可以提供分组,我可以在某家超市买完所有想要的商品之后,再换一家买剩余的。 灾难状态的封城在某些方面促进了这个想法的实现:首先被关在家里不能外出的我平白多了很多时间,用这段时间来学习一项新的技能显然是可行的;其次每次购物必须速战速决,尽量避免在人多的地方待太长时间而增加感染新冠的可能性;最后每个家庭每天只能有一个人出去购物,需要将清单尽可能细化然后实施(软件可以在某种程度上简化这个过程)。 当然,苹果自带的Notes或者Reminder都有类似的Todo管理,但是并不专门面向买菜。我设想的这个应用专门用来管理购物清单。在实施方面,我不打算从Hello world这样一步步从Swift的基本与反学起,而是先确定要做的App的主要功能,然后带着问题一边学习一边实践,最终把它演进成一个公开发布的工具。 实施 现在我终于到了我有一个价值千万的idea,就差一个程序员的阶段了。而巧的是,我就是个程序员。唯一的问题是,我的Swift和移动App开发经验约等于零(虽然多年前在项目间隔中参加过张帅的Android和黄磊的iOS workshop,但是时间隔得太久已经基本上还回去了)。此外,一个产品还有很多杂项,比如界面设计,图标,legal相关,App发布流程等等,这些对我来说完全都是未知数。 在实践中学习 通常来说,我比较喜欢通过循序渐进的方式来学习,从简单的例子开始,逐渐学习各个主题(如何设计界面,如何连接数据库,如何使用网络等等),这种方式的好处是基础会比较扎实,而且很多细节都可以得到充分练习。不过缺点是战线太长,往往需要耗时数月而没有实际产出。也因此如果过程中被一些其他事情耽搁,就很难再连续起来。 另一种方法是结果导向,然后用结果反过来驱动需要学习的内容。比如你想要开发一个本地的图片编辑App,那么肯定需要学习访问摄像头,访问本地文件系统等,而无需考虑网络和数据库。这种方式的优点是可以提供实时反馈,并且时刻有明确目标。 同样的,这种方法也有一些缺点: 会花费很多时间在Google和Stackoverflow上 很多资料都严重过时了,很多object-c的资料,以及很多swift老版本的资料 大部分人分享的内容,都比Hello world级别稍微复杂一点点,但是基本上不可能直接应用到你的应用中 因此在这个项目里,我尝试将两者结合起来。比如在第一个milestone里,我通过结果导向的方式,仅仅浮光掠影的学习必要的知识点,比如列表视图,iOS里的MVC,navigation之类。主要课程就是参考youtube上众多的入门教程:通过一个简单的例子,手把手的写一个Todo之类。这个过程可以熟悉编辑器,快捷键的使用,Swift的简单用法,界面设计器的使用等等。 这个阶段之后就可以开始“野蛮”编码了,开始的时候并没有特别的章法,就是照着例子写就好了。如果进行的顺利的话,很快就会有一个简单的原型出来,但是过程中会遇到海量的问题。小到如何添加图标,配置颜色,大到两个view之间如何通信,如何同步数据。不过作为一个有工程经验的程序员,大部分问题都可以通过Google+Stackoverflow解决。实在自己找不到答案的,还有很多热心的同事可以请教。 如果说我有一丁点成绩的话,无非是把别人用来思考变量名的时间花在了尝试Google关键字上了而已。 – 鲁迅 过了这一个阶段之后 - 有了一个可以工作的原型 - 就可以好好的打磨它了。于是我开始系统的学习udemy上的课程,比如如何使用使用protocol模式,如何使用MVVM,使用segue的正确方式等等。这时候可以适当的做一些重构和修一些简单的bug。 下图是从第一个原型到当前最新版本的用户界面的演进示意。从一开始的原生的TableView到定制的单元格,再到对数据按照超市信息分组并展现,每一次都比之前略复杂一些,而且每次的修改基本上都是基于客户的反馈来改进的。 此处的一个建议是:如果时间充裕,还是建议从基础学起,循序渐进,这样的方式可以确保基础比较牢固。而如果时间有限,又害怕被别的事情分心,那就可以边做边看,用到了再学,等建立起正向反馈之后再回过头来查漏补缺。 项目管理 在实施过程中我发现:一个可视化的,有迹可循的故事板非常有用。当然比这个故事板更加重要的是你需要制定一些规则并确保执行。比如,所有的卡片都应该从Backlog开始(即所有的需求都要经过仔细分析),开始前需要和stakeholder确认优先级,在开发过程中需要及时收集反馈,有明确的DoD(definition of done)等等。简而言之,尽量将其当成一个正式的项目来运作。 另外我还发现,之前在客户那里见到的诸多反敏捷的实践,我自己也往往会再犯一次。比如不及时更新卡的状态,临时插入Doing任务或者不限制在制品数量,不及时从stakeholder那里收集反馈,验收条件不明确等等。不过后来就好很多了,我尽量让其变得正式一些,而这个过程也确实可以帮助我更加聚焦在高优先级,更具价值的(而不是更fancy)的功能上。 这个大约是最简单,投入成本最小,又可以产生很好效果的实践之一了。特别是如果被一些临时任务中断之后(比如过了一个周末,或者项目上很忙晚上要学一些其他资料),一个故事板可以快速的帮你建立上下文并快速进入状态。 上线 经过了大约两周之后,我注册了Apple developer账号,并提交了第一个原型。第一个原型包含了三个列表(一个商品catalogue,一个待买列表,一个推迟列表)结果被无情reject了,原因是……功能太简单。仔细分析之后,我发现功能虽然不算单调,但是界面太过于原始,很多界面元素也需要调整。于是有花费了一周的时间来调整设计,比如字体的选择,字号,色彩对比等,以及实现了共享列表功能(可以将你的购物清单通过Airdrop或者微信等发给别人)。第二次提交之后,3个小时后就审核通过了。 V1的开发大约耗时3周时间,每天晚上学习并编码2-3个小时左右,周末会稍微多一些。随后V2也差不多2-3周,除了功能开发之外,还有些周边的定义,比如screenshots海报的设计和实现(详见下一小节)。不过我最近发现To Buy在iOS 14上有个bug(原因是iOS自身对NSFetchedResultsControllerDelegate的更新上有个同步的bug,如果你正好知道解决方法的话,请不吝赐教,非常感谢),所以如果你使用的以后发现什么异常的话,请反馈给我。此外,所有代码都在Github上,如果有感兴趣的同学也可以一起来完善(野生iOS程序员写的代码,请轻拍)。 To Buy应用 这个应用叫To Buy: grocery shopping list,目前已经在App Store上线了。这是我的第一个从界面设计,到编码实现,到架构及运维(虽然目前主要用户只是我和我老婆),以及图标和海报的设计,文案的编写等完全端到端的产品。虽然没什么用户,不过作为第一个App,我自己还是挺满意的。 特性列表 再经过了几个版本(当前版本V2.5.0)的迭代之后,现在的To Buy的主要功能就是:维护一个购物清单。用户可以: 通过拍照/相册里的照片,文字等来添加一个要买的商品 商品会按照超市分类,这样我就可以在A超市买所有果蔬,在B超市买日用等 可以把历史上买过的商品存到字典里,方便下次购买 可以将购物清单分享给另一个人(比如通过Airdrop) 可以把Apple自带的Photos/照片中的照片直接添加到To Buy中 所有数据会同步到iCloud,即跨设备可以完全同步 在迭代中,我发现了一个很有意思的现象:不论我当初如何笃定某个特性的设计,它的最终形态都会和开始的时候想法相去甚远。比如最早的想法是个内置一个大而全的商品字典,然后用户从这个字典里选择商品。但是最终发现这样的操作效率很低,特别是在手机上操作的时候。...

October 19, 2020 1 min

抽象的不同层次

一个关于抽象的故事 抽象能力应该是程序员所需要具备的、再如何强调都不为过的、最重要的能力了。通过抽象,我们可以从纷纭错乱的、独立的、看似互不干涉的大量问题中解脱出来,找到一个方案,然后一举解决一类问题。 由于抽象是提取了众多具体事物的某种特征并进行了简化的结果,抽象的概念有时候反而会阻碍我们的思考(大概人的大脑更喜欢具体的事物吧)。这时候我们与需要具体的实例来帮助理解。也就是说,在思维活动中,不论是代码编写,还是方案设计时需要不断的从实例中提取概念并形成抽象,又需要不时的将抽象具体化成实例来验证理论。Bret Victor曾将这个过程比作抽象的梯子是非常有见地的 – 你需要不断的爬上爬下,从高处看一看概览,然后回到地上做一些实际而具体的动作。 另一个有趣的事实是,抽象可以有多个层次,当我们终于脱离了众多细节,建立起更高层的概念时,这些高级(high-level)的概念又变成了新的细节,而我们又可以基于这些细节作出新的抽象。就好比我们现在使用JavaScript或者Python来写程序的时候,基本上不需要考虑诸如寄存器的编码含义,机器指令长度等等细节,也无需关注内存的分配和释放等等细节,而只需关注业务功能即可。 这么说起来太过于抽象,我们来看一个具体的例子吧。 背景介绍 在这篇文章中,我希望通过对一个模拟项目的自动化测试(使用cypress)的重构来描述一些代码编写/重构时的模式。其中涉及到的重构方法比较常规,并没有高深的技巧,不过我觉得通过详细解析一个从可以工作的代码演进为较为整洁且便于理解/修改的测试用例,整个过程还是比较有趣的,希望你也可以有所收获。 所谓模拟项目,是对我从2019年1月到10月所经历的一个项目的模拟。纯粹从业务来说,该项目的业务规则/逻辑并不是十分复杂,不过技术细节上有很多值得反思的地方。我们的自动化测试也是经历了N个版本的迭代,最后我也成功将这篇文章中描述的模式应用到了该项目中,效果也是比较令人满意的。 这里的模拟应用Questionnaire是这样一个服务:通过类似调查问卷的方式来输入用户技能,角色,技术水平,项目偏好等等,根据后台的规则引擎来返回一些最适合用户的offer列表。后台的计算逻辑我们这里刻意将其淡化,这里只关注信息的采集部分。 应该注意的是,这里的调查问卷是动态的,比如如果对问题Q1选择了答案A,则下一个问题会变成Q303,如果选B的话则需要回答问题Q1024等。也就是说,问卷的路线有可能是多条的,而且每一条都是同等重要的(至少在测试里需要完整走通一遍)。 从界面上来看,我将整个问卷简化成了3步,实际场景里则可能是10+步,而且每一步中需要回答的问题长短不等,有些问题展现/隐藏还会依赖于前面某一步/某几步用户的答案等。用户只有完成了当前所在的步骤的所有必选项之后,才可以进入下一步(Next按钮才会可用)。 抽象101 - 函数 从功能测试编写的角度来看,将每个步骤视为一个独立的单元是一个合理的做法。每一个步骤需要做的事情也非常类似: 验证该步骤的标题是正确的 填写该步骤的所有必选项 点击下一步按钮 在写一个section的测试时,直观的通过cypress提供的API的结果大致为(假设这里的所有data-test标记我们已经在应用代码中打好了桩): it('Verify the basic information section', () => { cy.get('[data-test="step-title"]').contains('Basic information'); cy.get('[data-test="email-address"] input').type('juntao.qiu@gmail.com'); cy.get('[data-test="assignment"] input[value="assigned"]').check(); cy.get('button[data-test="next-button"]').click(); }); 第二个section的流程大同小异,都是先用selector找到页面元素,如果可以找到的话,通过cypress的API来模拟用户的实际操作: it('Verify the details section', () => { cy.get('[data-test="step-title"]').contains('More details'); cy.get('[data-test="ps-role"]').click(); cy.get('[data-value="dev"]').click(); cy.get('[data-test="developer"] input[value="frontend"]').check(); cy.get('[data-test="rating"] [for="rating-4"]').click(); cy.get('button[data-test="next-button"]').click(); }); 如果逐字对比的话,每行代码几乎都不一样,但是如果仔细看又会发现很多重复。消除这些重复显然可以让代码干净一些,可读性也可以得到提高。一个立即可以想到的重构方法是抽取函数,将验证标题和点击下一步抽取如下: const checkStepTitle = (title) => { cy.get("[data-test="step-title"]").contains(title); } const goToNextStep = () => { cy....

July 9, 2020 3 min

我的第一本英文技术书

2021年6月更新 不知不觉已经过去快一年了,我来更新下这篇的后续。 首先我想分享给大家的是:这本书的纸质版已经由APress出版社正式出版了! 从原来的博客发布至今的近一年中,这本书经历了很多次的编辑和重构。事实上,在这本书有了初稿之后,我就曾请求同事Martin Fowler帮我review并提供一些早期的反馈,不过他当时还在忙于《重构》第二版的审校工作,所以只是放入了他的backlog里,直到去年7月才review完成。Martin的批注非常仔细,从目录结构,到遣词造句,再到实例与主题的关联度,以及参考文献等等方面,差不多有近10页。我根据这些批注重新组织了书的结构,并删减了差不多1/4的内容,使得结构和内容更聚焦。此外,Hannah又在此基础上进行了英文的编辑和润色,以及对一些代码实例的建议。 去年圣诞前后,APress的编辑找到我,表示他们有讲此书出版为纸质书并在他们平台上发售的意向。在经历了数个月的编辑,审校,勘误之后,这本书终于出版为纸质版本。在上周末我收到了出版社寄来的几本样书。看到自己的名字印刷在封面上是一种奇妙的感觉,开心当然是有的,不过随之而来的又是一些隐约的担忧:担心书中技术终会过时,担心有不好的comments等等。不过该来的终觉会来,这大约也是出版的一部分吧。 另外,感谢那些鼓励我的、默默提供勘误的、报告bug的人们,没有你们的帮助,这本书不会最终成为可能。 我的第一本英文技术书 太长不读 我写了一本关于React+TDD的英文书,你可以从这里免费获得(更新:和APress出版社的合同中明确要求需要从其他渠道将其下线)。在我写这篇文章的时候,读者大约是1000+。虽然读者不算多,但是通过一些渠道收集到的反馈还算不错,已经超出我自己预期很多了。如果你读完觉得还不错,而且愿意帮我宣传一下我会非常感激,如果你想要做一些修订甚至翻译的话那就更棒了(有一个俄罗斯小伙在帮我翻译俄文版,不过他貌似拖稿十分严重)。 缘起 实话实说,写这本书纯属意外之举,最开始的时候对于内容和形式是完全没有计划的,更不用说用另外一门语言作为载体了。应该是在2017年年末的时候,我在华为的无线部门做一个网管产品的Web化,人员能力提升显然是Web化的核心了 – 毕竟功能需要开发人员一行行去实现。当时该部门中的大部分开发同事的Web技能都比较薄弱,一些有经验的同时则每日被业务需求缠身无法顾及能力构建,因此引入外部的培训来填补这个空缺。 这个看起来是Web开发101的培训还有一些其他需求,比如用户故事拆分、自动化测试和TDD等等敏捷开发的基本内容。我为团队设计了一些循序渐进的例子作为培训的素材,并在后来的几轮迭代中进行了内容的删减和补充。培训的结果还是不错,有一半以上的同事对Web开发产生了兴趣,有人则开始意识到自动化测试(以及TDD)可以减少回归测试的工作量。 正如你所料,这个培训的一个副产品就是这本书的原型了。在培训结束之后我正好有了几周的beach时间,我用这些时间将内容变成一个教程:从一个简单的例子,逐步完善成一个前端应用。其中涉及了诸如Tasking,ATDD,用户故事等。 过程 初稿 原型是很容易的,素材是现成的,只需要将其以符合逻辑的方式连接起来即可。比如我想要描述通过TDD的方式开发一个应用的全过程,那么首先我需要讲清楚我们要做一个什么应用,此外我需要说清楚如何用TDD来完成这个应用。根据经验,人们喜欢循序渐进的方式来阅读,先做一个简单需求,然后逐步增强,并在过程中将学到的内容应用,然后推广到后续需求的开发中,并通过实现更加复杂需求习得我想要传递的知识/技巧。 TDD的101(通过例子来说明Tasking和测试驱动的过程) 介绍demo应用,搭建应用所需的环境 设计并实现一个简单需求 实现一个更复杂的需求 循序渐进…… 编辑初稿的过程中,正好有个掘金小册的编辑联系我有没有意向写个小册子。虽然最后由于篇幅和主题的选择方向没有合作,但是和编辑沟通的过程帮助我很好的梳理了草稿的结构。 英文版 在草稿写完之后,我找到几个同事做了快速的review并做了一些修改。到了2018年3月,我开始准备来澳洲的LTA,既然英语会是我接下来几年的主要语言,那么提升英文能力就成了优先级很高的事情。而学习语言的窍门就是:没有什么比实际使用一门语言更能提升语言能力的了。 于是我开始尝试把草稿翻译成英文版。开始的时候我还会保持双语版本的同步,到后来带宽不足的时候就只能把精力放到英文版上了。第一个完整的英文版发布于2018年5月,后面虽然陆续有些小的修改,主体部分没有太大的变化。 到了澳洲之后,读了很多英文原版的书籍/文章之后,发现了书中很多的语言错误,又在一个假期里集中修改过一轮,可读性有了一些提高。 重构 到了2019年的9月,经过了近1年2个纯React的项目后,我对与React及其生态的理解,以及在应用TDD(特别是和传统上认为的TDD很难在复杂/时间不允许的项目上实施)的认识上都有了新的发展。于是又将其中很多章节重写了,比如丢弃了pupeeteer改成cypress,采用react-testing-library而不是enzyme等等。另外,结构上也做了重新整理。 由于这些颠覆性的修改,我将其重新命名为Mastering Test Driven Development with React,并声明其为前一个版本的第二版。 到了2020年4月,由于covid-19在澳洲的肆虐,我们开始全面WFH。一方面突然有了很多时间,另一方面我意识到线上沟通时英文在有些时候还是会变成blocker,于是我又花费了一些时间来提升英文。于是又开始了一轮的re-wording。 在你今天阅读这本书的时候,你大约会惊叹于英文表达之地道,语法之准确以及用词之精准。这些都和我的英文水平没有太大关系。在5月的一天,一位澳洲同事Hannah Bourke写邮件给我,表示她通读了这本书,非常喜欢其中的讲述方式和实例以及节奏,她表示愿意帮我近一步润色。她通过PR的方式重新整理了本书的语言(目前已经完成了70%),由于Hannah本身就是前端Dev,所以很多表达方式也被重写为更容易被读者理解的方式。 一些收获 关于英文写作 语言的学习是个漫长而痛苦的过程,同样的内容,要从一门语言中的表述要翻译成另一门语言需要的更多的是重写而不是literal translation。从LTA开始我就开始刻意的用英文写作,还尝试把一些文章翻译并投稿给英文版洞见。 通过这些练习,我觉得我至少不再惧怕这件事情本身。英文表达当然有不地道的地方,但是读者也不是语言学家,大多数时候他们都可以准确无误的get到你的意思。即使有些复杂概念无法一次理解,通过评注或者提问等等,总是会搞清楚的。 关于自信构建 在这本书的写作过程中,我个人最大的收获应该是:当你制定了一个目标,不论这个目标开始开起来有多么的不切实际,一旦你开始细化这个目标并逐步实施,你就已经离这个目标不远了。当然,和每个任务一样,事情走到最后可能会和最开始的目标并不完全契合,但这大约是我们无法掌控的那部分了,就随他去吧。

July 1, 2020 1 min

<del>程序员</del>英语学习指南

程序员英语学习指南 这篇文章从我个人的经验出发,总结了英语学习中一些比较重要的关键点。简单来说,就是尝试投入专门的时间,集中精力突破7,000到8,000的核心词汇,然后在这个基础上逐项提升英文的输入/输出能力(听、说、读、写)。一个省时省力的实践是:听和说可以一起练习,读和写往往也连在一起。如果可以在听的同时练习说,以及读的时候同时写,那么显然可以事半功倍,并促进形成比较积极的反馈,从而激励自己投入更多的时间和精力来提高英文能力。 在进入正题之前,我们需要看一些数据和事实,来帮助我们认清把英语学好是一件什么规模/规格的任务。 语言学习是困难的 语言的学习和很多生活中其他工作/任务有很大的不同,这些差异往往会导致学习者的挫败感。首先,学习外语的反馈周期往往很长,即使你每天坚持扩充词汇量、阅读文章、甚至与采用外语来写作,而从开始投入到看到成效往往需要较长时间。其次,自然语言中的规则往往不固定,比如英文中很多动词的过去式和过去分词都是不规则的,这些不规则使得学习者在熟记“公式”外,还需要记忆很多变体(比如单词seek的过去式和过去分词都是sought)。最后,语言通常总是和文化关联在一起,这也意味着它无处不在却又难以描述,而且这些关联又非常离散,难以集中突破。 虽然个体之间会有各式各样的差异,但是总的来说,这些客观的挑战对于每个外语学习者都是存在的。如果你咨询任何一个外语比较好的同事,大约会得到很多不同版本的经验或者秘笈。我这里想要分享的自然只是我自己的经验,未必系统,当然更不可能全面,但是我相信可能对你也有效。 简而言之,无论是在听、说、读、写四大元素的任何一个方面,要想取得比较大的进步,首先要做的是认真对待词汇量。丰富的词汇量是学习更近一步内容的基石,一方面,她可以帮助你更好的理解其他人的意图,捕捉到细微的感情差异。另一方面,她可以帮助更精确,更快速有效的将自己的思想传递给他人。 重中之重:词汇量 词汇量的重要性 开宗明义,永远不要“妄图”把英语说的和native一样好。这个目标既不切合实际,也完全没有必要。我估计不是每个人都同意这个观点,为了让你放弃这种想法,我们可以先看一下native speaker的平均词汇量: 就近50万的受访者的统计显示,50%的人在8岁时会有10,000的词汇量,而在15岁时这个数字会增长到20,000,而这个数字大约会在成年后到达30,000并基本保持一个平稳的状态。作为对比,我国的CET4级考试词汇量大约为4,000,基本相当于4岁的native,而雅思则需要近8,000的词汇量,约相当于7岁的native的平均水平。 不过好消息是,如果你愿意花一些时间来攻克7,000-8,000高频词汇的话,你就可以读懂大约80%-90%的普通材料(大部分新闻,公告,大部分技术书籍,一些简单的儿童读物等),而且你又可以通过这些材料进一步将自己的词汇量扩充到更高。 英语的另一个有趣的(可恨的)特点是可以通过非常简单的基础词汇+介词形成丰富的短语,如果可以把这些短语正确的应用,你甚至无需花费太多时间来学习更高级的词汇即可应对日常生活学习工作中的各种场景。把这一点用到极致的应该是词汇量相当于8岁儿童平均水平的懂王特朗普了,不过话又说回来了,这可能也是一项非凡的技能,比如可以和动词stand搭配形成短语并具备独立含义的结构就是十多个。 一个微小的例子 为了更好的说明上面的问题,这里有一个小例子。下面这段话是The Economist(经济学人)里的某篇最新文章中的两段。如果你的词汇量在8,000以下,可能读起来会比较吃力。如果有12,000,则读起来不会有太大障碍。而要像读中文那样顺畅的读懂大部分The Economist中的文章,则需要20,000以上的词汇量。 Many a regime has been toppled by a plague, but so far covid-19 is having the opposite effect. Most leaders have seen their approval ratings rise, even as the disease has killed at least 250,000 people. Morning Consult, a pollster, has found that a group of ten politicians have enjoyed an average gain of nine percentage points since the World Health Organisation declared a pandemic on March 11th....

May 13, 2020 3 min

那些年,我见过的那些"废柴"

在我们的日常工作中,和其他人一起合作在所难免。与三观未知的*他人*朝夕相处往往不啻于一场冒险。那些传说中行云流水般流畅的合作似乎仅存在于书本中,现实世界中的合作则常常难以契合,大多互相掣肘,不得通畅。毫无疑问,在众多的项目中,与我合作过的很多同事都令我大开眼界并深受启发,并在或长或短的合作中受益良多。作为对比,同样有很多同事(我把一起工作的人们都成为同事,而并不特别区分内部外部,比如团队上的其他合作方也可以视为同事)则从相反的方向令我印象深刻。

August 29, 2019 1 min