去年我写过一篇文章《我的成长故事》,介绍了自己从童年到成年这段时间里一些重要的转折点和里程碑。而贯穿我整个生命历程的核心线索,就是「编程」。
如果没有编程,就根本不会有今天的我。
在离开互联网圈、告别了紧张的996之后,我走上了讲台,开始教孩子们学习编程。这几年来,我写了不少关于编程的文章:《为什么你应该开始学习编程了?》、《怎么给小学生上编程课?》、《编程到底是怎么一回事?》、《学编程到底有啥用?》……
但是,我始终没有写一篇文章来介绍:在自己的生命中,编程到底扮演着怎样的角色?编程究竟给我带来了些什么?今天,我将以「编程」为线索,再次重新梳理一下自己的成长历程,希望能给大家一些启发。
童年的编程启蒙
我家第一台电脑是夏普的PC-1500,它比我的年纪还大。

在汉卡横行的1984年,我的父亲张时钊在这个只有8K内存、单行屏幕的机器上开发了无字库汉字系统,用纯软件方法来显示和打印汉字,轰动全国。后来获得了科技进步二等奖。现在这台PC-1500和当年父亲的汉字系统说明书一起,正躺在北京中关村创业博物馆里展览。

上面是一篇1992年我六岁时的日记,记录了父亲给我们上计算机课的经过。早在我四五岁时,父亲就向我展示了他在PC-1500上编写的一个走迷宫的小游戏。我可以用键盘来控制一个不断闪烁的黑点,从随机生成的地图上从屏幕左边走到右边。那块可以操控的神奇屏幕,对年幼的我来说简直就是一个奇迹。
后来父亲用自己卖汉字系统得来的钱买了一台IBM XT,开始教我BASIC语言。那时的我抱着厚厚的参考书,在键盘上用一天的时间把程序输进去,然后让父亲帮我修改,并努力搞明白每一行程序的作用。
那时候还没有Windows,只能在DOS命令行来操作电脑。cd、dir之类的命令很快被我玩得纯熟。每当父亲外出时,我就会从书架上把父亲的软盘一张张拿出来,插到电脑里去,一个目录一个目录地遍历,找找看里面是不是藏着小游戏。找到一个,就赶紧copy到硬盘上自己的目录里去。因为DOS下的常规内存只有640K,而有不少游戏需要非常苛刻的内存条件才能运行,于是我又无师自通了对config.sys和autoexec.bat进行优化……
后来Windows出现了,我很好奇这个东西是怎么做出来的,于是尝试着用Quick Basic来写一个。可当时的我只能画出一些简单的图标和界面,但怎么也实现不了拖拽、菜单之类的效果,只好悻悻作罢。
1998年高考,我没有考上中科大的少年班。父亲带我来到西北大学,拜见当时计算机系的主任张文恺教授,张教授给我出了一个算法题,因为对题目比较熟悉,他还没说完我就开始敲代码,很快就完成了。张教授很吃惊,破格录取了我。
如果我当年不会编程,就根本没有机会迈入大学的校门。自然,也就不会有后来的故事了。
自己写游戏玩
在大学里,我成立了一个水滴工作室,还自学HTML+CSS做了一个网站:

没游戏玩,我索性就自己写游戏玩。我用了大概一周时间,用QuickBasic写了一个模拟运营的游戏《市长大人》:

这个游戏里有无数相互作用影响的变量(农业、人口、商业、消防、安保、计生……),还有很多随机发生的事件(火灾、地震、罢工……),后期还可以研发出各种逆天科技。最终是被轰下台还是升职加薪,全看你的运营能力。因为有存档功能,你也可以用S / L大法来测试不同的选择……
写完游戏后,我就把它装到机房的硬盘里。不时会有同学去玩,然后评头论足一番:这里不平衡、那里不科学……我收集到反馈后,就对游戏作出调整,然后再发布一版。这应该算是我有生以来的第一个「公开发布」的程序产品了。
后来动画片灌篮高手在校园热播,我又做了一个经营+模拟的游戏《灌篮高手》:
恩,为了方便那些还不会用FPE的同学,我还干脆做了一个存档修改作弊器:
初入职场
2001年大学毕业之后,我跟着大学里最要好的同学张立平来到了北京四中网校陕西分校。网校是怎么一回事呢?其实就是把老师上课的PPT、视频打包成一个文件,然后放在服务器上,学生在家里装一个客户端,通过电话线拨号链接到服务器,把这些文件下载到自己电脑上学习。
我的第一份工作,是从上门给人装软件的技术员、接电话的客服代表开始的。那时候的电脑普及程度远远不如现在,很多人其实都是不会操作的。我经常通过电话远程指挥连鼠标都没摸过几次的爷爷奶奶们操作软件,你能想象那种场面吗?一通电话下来,小命都快没了。我就奇怪了:这些说明书上不是都有吗?为什么他们还不会呢?
后来我发现,问题原来就出在附送的说明书上。说明书本来是记录操作软件的程序的,然而编撰者却删繁就简、省略了很多中间步骤,还整篇都充斥着各种术语,这分明是技术人写给技术人看的,终端的小白用户是根本看不懂的。于是我重新设计了每一个步骤都配上图、通篇大白话的说明书,并把所有已知的问题和详细解决步骤都列在后面,客服电话一下子就少了很多。
我还有一个习惯,就是把自己的工作程序编写成详细文档。这样一来方便自己查阅、不用记忆;二来也分享、培训、交接都很方便。如果一个岗位的工作程序只装在你的脑子里,那么每来一个新员工就得培训一次,太累了不是?再者,如果这个岗位只有你能干,别人都干不了,工作离开你就不转了,那么你还有升职的可能性吗?
我始终坚信:一个人的价值,不在于他得到过些什么,而在于他留下了些什么。把自己的想法、工作流程编成程序,时刻为了自己离开、甚至死亡而做好准备,让有价值的程序独立于自己而存在并持续发展,这是一个成熟者应该做的事情。
不久后,我成了技术部的leader。领导想做一个教学资源平台,但是我只会用HTML+CSS做静态的网站。于是我决定去深圳找一个专门做开发的大学同学,在他的宿舍里闭关半个月,硬生生学会了ASP+MSSQL数据库。
会用HTML+CSS表现内容,会用ASP操作逻辑,会用数据库存储数据之后,我就像是一下子打通了任督二脉,觉得自己什么东西都可以做出来了。后来跟着领导创办了脑图英语,做了教学资源平台、远程学习平台、个性化学习解决方案……

因为学生分布在全国各地,为了解决当时「北网通、南电信」的屏障,我想到了两个运营商各部署一台服务器,然后通过测网速来选定最快的服务器提供资源的办法。后来我才知道,原来这套逻辑这就是现在用来支撑各视频和直播网站的CDN技术。
带领技术部整理资源、制作FLASH课件的过程中,我发现大量的操作都是同质重复的。于是用按键精灵给每类工作都编写了脚本,只把需要人工操作的部分空出来,完全重复的工作统统自动完成。就这样,我带着技术部四五个兄弟,每天完成数以万计的资源整理和开发工作……我还写了一个工作量在线统计排行,每天搞搞比赛什么的。
编程把我从枯燥无味的重复工作中解放了出来,获得了大量空闲的时间。那我用这些时间都干了些什么呢?当然是玩游戏了。
用编程思维玩游戏

我深入研究的游戏很多,而扫雷是第一款让我开始小有名气的游戏。2005年还没有Clone和Arbiter这样支持录像的专业扫雷软件,大家都是用屏幕录像来参与排行的。在努力练习进到70秒后,我一度遇到了瓶颈,再也无法提升成绩。于是我想到了用变速齿轮调慢系统时间来作弊的方法,并借此在一片质疑声中拿到了中国第一。
为了让作弊的录像看起来更真实,我必须用很慢的速度进行鼠标操作,这样在加速之后才能看起来是正常速度。这样一来,我发现自己有了很多时间来观察和思考局面了,于是我开始思考:同样的局部,怎么用最少的点击数和最少的移动距离来完成?就这样,我发现自己之前无意识地操作虽然看起来飞快很流畅,但是其中很多都是废操作,根本没有用!
在把局部操作最优化的同时,我不知不觉练成了一种能力:在进行当前操作的同时,开始预读下一个局部并思考出接下来的操作。这样就节省了之前在处理完一个局部后,在思考下一个局部怎么处理时的停顿时间,让两个局部的操作可以连贯起来了。
原来,我不在不知不觉间,把编程时那一套性能优化的思路拿过来应用了。分析日志定位性能瓶颈;再通过优化算法、同步改异步来提升性能……慢慢地我发现,我根本不需要用加速齿轮这样的东西,就可以SUB60、甚至SUB50!

很快,我就凭自己真实的水平,拿到了货真价实的中国第一。我就此写了一篇《走向中国扫雷第一之路》,完整地记录了自己从作弊第一到实力第一的进步历程。随后我又创办了中国扫雷网(www.saolei.net),为中国扫雷玩家提供了一个交流和进步的平台。

因为我懂编程,所以网站可以100%按照我的想法来做。我喜欢实名上网,就在扫雷网强制推行实名制;我想给大家安排一些头衔,于是就想出了「雷帝、雷圣、雷神、雷仙」系列;我想要什么类型的排行,就加什么排行……因为会编程,我感觉到了自己几乎是无所不能的。于是在很长的一段时间里,我的座右铭都是:「没有不可能的事,只有不敢做的事」。
很快我就发现一个问题:每一个用户上传的录像我都得自己看一遍,检查有没有作弊或其他问题,这样也太累了!于是我招募热心玩家成立了管理小组,把审查录像等日常事务下放给他们,自己只负责制订管理规则、定期招聘和争议裁决,一下子就清闲了。
把自己要干的事情写成程序,交给别人去干,这也是一种编程。后来有位新上任的管理员也会编程,于是他干脆写了一个程序来审核录像常见的数据错误、明显的作弊行为等等,只有程序拿不准的才让人来把关。这是后话不提了。
2008年,在高级SUB40之后,我对扫雷的兴趣渐渐减退,玩起了魔方。

魔方仍然是一个竞速的项目,于是我继续使用编程的那一套性能优化方法论:通过「空间换时间」的思路,记忆了大量多向公式,尽可能地减少了多余步骤;运用「局部最优化」的思路,对每一个公式和手法的细节进行打磨;运用「同步转异步」的思路,在做公式的过程中判断下一个要做的公式来加强连贯……

最终,在2008年广东WCA公开赛上,我三场比赛创造五个NR(国内记录)一战成名,从此开启了持续两年的全国制霸模式。我参加了26次WCA比赛,累计获得10冠9亚,打破记录无数。每次去比赛都有一堆粉丝签名合影,存在感十足。
同样出于习惯,我把自己总结出的各种经验心得、公式手法都整理发布在了魔方吧论坛上。魔方还原的主流方法是CFOP,其中第一个阶段叫做Cross,也就是还原魔方某一面的四个棱块,使其做成一个十字。其他阶段的CASE相对有限,都可以找到对应的公式,只需要练习就好。而Cross则变化无穷多,是最难描述、最难教学同时也是最难提升的部分。各路高手都说“靠经验慢慢积累就好”,但是谁都说不清楚到底应该怎么做。
我偏不信这个邪,硬是把自己做Cross的程序系统地写了出来,总结成了十个字「忘、记、观、察、色、升、修、行、完」。迄今为止,我这部《十字天书》依然是魔方界做Cross部分最系统完整的教程,没有之一。
程序就是这么一回事:一旦被写出来,它就不属于你了,而是成为一个独立于你存在的生命。 随着魔方界新人辈出,我的统治时代宣告终结,渐渐从魔方圈里退隐了。而我写的那些教程,至今仍活跃在魔方吧论坛前几页,仍然在发挥它们应有的价值。反过来,我的名字也因为它们的存在,而不会被人彻底地遗忘。
成为职业程序员
2011年初,我在爱人的鼓励下来到北京,决心做一名真正的程序员。面试前一天,我带着满满一箱书踏上了开往北京的火车,一路上同桌人在打牌聊天,而我却在背Linux的命令和VIM的快捷键。我对自己说:混不好就别TMD回来了!
然而就是这次面试,把自恃甚高、信心满满的我打击得一无是处。野路子出身的我,简直就是一只井底之蛙,对新技术几乎一无所知;惯用Windows平台,完全不会业界标配的Linux架构;之前建设的网站流量太小,对高性能优化完全没有经验……
最后,我还是凭借自己做多年web开发,连ie6也能兼容的前端实力,拿到了offer。接下来的日子里,我在工作之余拼命地看书、读php框架源码,每天只睡四五个小时。终于在半年之后,我如愿以偿,加入了后端开发团队。
因为创业公司人少,一个人得身兼数职。我写完前端写后端,写统计报表,做运维,做测试……于是成了所谓的「全栈工程师」。我开始使用异步队列、优化索引、部署缓存和CDN、进行AB测试……在享受各种技术带来的便利的同时,也踩了一个又一个的坑。我见识了框架的力量,也体验了自己造轮子与使用开源技术之间的区别,创建了自己的app框架、测试框架、less框架……
到最后我发现,所有的技术原理最后都是相通的,无非就是在「时间」和「空间」这两个不同的维度上进行权衡交换罢了。时间敏感而空间不敏感的场合,用空间换时间,比如缓存、队列、哈希……;空间敏感而时间不敏感的场合,用时间换空间,比如分治、模板、压缩……
为了提升工作效率,我编写了aoeu.sh来给各种常用linux命令设置alias;使用StrokeIt和Alfred来一键启动各种软件;编写自动部署脚本来完成服务器的日常运维工作……然而一味地追求效率,往往会导致缺失对安全性的考虑。我先后造成了两次严重的事故:一次drop线上库、一次rm -rf线上服务器,深刻地认识到了「欲速则不达」的道理。
也就是在这段时间里,我开始接触并切换到了Dvorak键盘布局,一试之下便欲罢不能,跟使用了二十多年的Qwerty布局说再见了。后来对自己的中文输入速度不满意,想采用「双拼+形码」的方案,但发现所有的双拼方案都是基于Qwerty布局设计的,于是基于rime输入法自己设计了一套双拼形码方案,保证常用汉字都可以盲打四键内上屏:
能快速盲打中文给我带来的最大好处就是:写文章时可以把绝大多数的注意力放在内容的组织构建上,而不是消耗在打字和纠错的过程上。这使我很容易在写作时进入心流状态,也让我养成了读书时用电脑做笔记的习惯。把读到的信息用自己的语言重新描述出来,除了能让自己的理解更进一步,还可以积累起丰富的写作素材库,需要时一搜便是。
从工程师到技术经理
2013年底,我来到赶集网,加入了拳头产品招聘事业部。赶集的研发团队近千人,分成许多不同职能的团队,每个团队只专职负责系统中的一小部分。从创业公司过来的我,一开始还不太适应:做需求得跟PM和QA打交道;提交代码需要同事review;写sql需要dba审核;上线需要先在两个环境通过测试……

在这里,我第一次接触到了真正意义上的海量数据和高并发。我拿到的第一个棘手任务,就是优化每天500W+请求的预约刷新业务。啥是预约刷新?赶集网主要核心的页面是一个列表页,各商家都想往前排,那么除了重新发贴外,就是付费预约在某一个固定的时间点把帖子刷到最上面。那么问题就来了:大家都集中在一个时间点刷,结果就会刷不过来造成延迟,本来9:00刷新到第一页变成了9:05刷新到第二页,用户不干了!
为了解决这个问题,我想到了利用大多数人只会改小时不会改分钟这一点,通过在小范围内随机生成分钟数字,来平坦整点波峰的办法。同时配合优化web请求、后台任务多线程、检索增量更新等多种手段,最终解决了延迟问题。
在工作之余,我依然在想办法用编程来提升效率:写了一个flushDNS插件,用来清理chrome的DNS缓存;写了一个jira.sh命令行工具,用来把jira中的标题放到svn里,把svn的更新文件列表放到jira里,节省来回复制粘贴的操作……
在业务进入平稳发展期后,我开始潜心钻研如何提升代码的可读性、可维护性、可复用性。有代码洁癖的我,想把自己经手过的代码都变成艺术品,而不仅仅是实现功能的技术产品。把一堆不堪入目的烂代码重构成赏心悦目的优雅代码后,我总是能体验到了无与伦比的成就感。
不久后,我便成为了团队内技术级别最高的工程师之一,并拿到了年度优秀员工,获得了大家的认可。

到了2015年,赶集和58同城合并,大量老员工离职,我被安排接管黄页业务开发团队。一开始我非常不适应,因为看着手下们写出来的代码,内心中总有喊「放着我来!」的冲动。
然而我很快意识到:衡量一个leader是否优秀的关键,不是自己的个人能力有多强,而是能把整个团队带到什么地方去。我不能每场战斗都冲在最前线,而是要让手下的人去完成原本自己擅长的工作,给他们足够的机会去试错,包容他们的过失与不足,并帮助他们不断成长。
于是,我开始把自己的工作重心从写代码转移到了需求评审排期、资源调配、进度管理、提供技术支持、代码质量管理,以及招聘面试上……
在需求评审会议上,我对每一个需求的可行性和合理性进行全方位评估,并根据员工的实力和过往表现来估算出合理的工期。规划时间总会留有一定弹性,以保证各种意外发生后局面仍能在控制之中。我把自己作为最后的备用资源,因为如果连我都上了,就没有能力再应对任何意外和紧急需求了。
我在团队内部启用了teambition来管理任务进度,把需求拆解成「计划中、已排期、已提测、已上线」四个阶段来管理。每个需求标记上责任人和到期时间,然后每天监控进度,发现有问题苗头便及时跟进,并提供必要的支持。于是黄页团队的需求,从来就没有延期过。
在每周例行的code review上,我带领小伙伴们一起复盘业务代码中的不足与缺陷,探讨着怎样的代码实践是更好的。我还把历年来沉淀下来对代码质量的理解,整理成了六讲《浅析代码质量》在团队内部分享:

为了给团队不断补充新鲜血液,我展开了持续一年多的长线招聘的技术面试;并严把入门关,确保新加入的人能提升团队平均水平。在面试过程中我感触良多,于是开启了「镓话」公众号,写出了面试杂谈系列文章:《面试程序员时都应该考察什么?》、《N年工作经验真的有用吗?》、《工资真的越高越好吗?》、《怎么才算精通xx?》、《程序员最核心的竞争力是什么?》、《我是怎么面试程序员的?》……
在团队不断壮大、一切正向美好发展的过程中,我却发现自己逐渐丧失了继续前进的动力。因为我越来越感觉不到自己的进步,工作中也越来越缺乏创新、挑战和价值感,我仿佛能看到自己5年、10年后的模样:不外乎带更大的团队,拿更高的工资罢了。
随着两个孩子出世,做了六年职业程序员的我,终于开始怀疑自己职业生涯的意义:我真的愿意在一台机器上,做一颗看上去华丽但不自由、甚至可以被随时替代的高级螺丝钉吗?
探索编程教育
孩子满一岁时,黄页团队面临业务重组,我趁此机会下定决心,选择辞职陪伴孩子度过关键的成长期,来到爱人工作的运城国际学校,成为了一名全职奶爸。当然,我已经想明白了自己接下来要干什么。
从我玩游戏那时候起,我就开始大量地写各种教程;在投入工作以后,我也总是乐于总结和分享自己的经验和心得……我好像特别喜欢给别人讲明白一件原来他不明白的事,偏偏我好像还很擅长这个!那我为什么不干呢?编程已经地深刻地改变了我的生命历程,成为了我的一种生活方式。我要向全社会普及编程,让更多的人享受它的好处和乐趣!
2017年春,我写了一篇爆款文章《为什么你应该开始学习编程了?》,开通了公众号「编程镓教」,也在学校办起了初中编程社团。开始同时探索线上和线下教学的有效途径。
孩子上了幼儿园之后,我在学校开了编程课,正式开启了自己的教学生涯。我选择了相对冷门的code.org而不是火热的scratch来进行编程启蒙,因为我认为scratch自由度太高,对初学者来说过于复杂了,不利于教授和理解基础概念。在经过code.org的系统课程打下基础后,再进入scratch创作作品和进一步学习,效果会更好。
渐渐地我发现:做教育并没有我想象中那么简单。我写文章时,可以只考虑自己怎么写清楚,而不用管读者能不能读懂;而搞教育,必须从学生的角度出发,用他们能理解的方式来讲述他们感兴趣的内容。除此之外,还要研修教育心理学,了解学生的身心发展规律,磨练和孩子的沟通技巧……
多年程序员的经验让我明白:做任何事情都不太可能一次做好,而是需要不断地迭代来优化。于是我设计出了上课记录单,每节课后都记录教学日志,然后反思上节课暴露出来的问题,改正之后再给另一个班上。因为同一节课会在不同的班上多次,这就给我了对课程进行反复迭代甚至AB测试的机会。慢慢地,我学会了如何合理地设计课程,如何针对水平不同的学生实施分层教学……
我还在每节课上专门抽出10分钟来,让学生写一篇学习日志。因为在集中化授课的课堂上学生的发言机会很少,当众表达自己真实想法的阻力很大。让学生当堂写日志,是一种非常有效的与学生进行沟通、获得课堂反馈的手段。让学生把学到的内容用自己的语言描述出来,不仅可以加强印象,也方便日后复习。另外,老师通过巡查日志的写作情况,可以掌握学生对课堂内容的掌握情况,哪些地方理解有偏差,有哪些疑惑的问题,并当堂补充讲解,并在随后对课程加以针对性的调整。
各种头疼的问题逐渐被一一解决,教学活动进入正轨后,我就开始研究如何用程序简化一些周期性重复的工作,比如用python脚本来通知每天各班进度落后的学生来补课、打印当天的上课记录单、学期末统计学生成绩……
经过近三年来的编程教学实践,我对小学及初中阶段「为什么要学编程」、「应该学什么」、「应该怎么学」有了更清晰的认识。我应邀在ET沙龙做了一次题为《怎么给小学生上编程课》 的分享,从教师的角度分享了我这些年来积累的编程教学实践经验;还在全人之美教师联盟做了一次题为《学编程到底有啥用?》的分享,从家长的角度分析了编程教育的价值,以及我对少儿编程教育的态度:
编程就是信息时代的物理学,它可以帮助我们更好地理解这个世界,并为我们提供改变世界的可能性。正如我们每个人都需要了解必要的物理常识,却没必要成为物理学家一样;每个人都应该学习编程,并利用它来改善自己的工作和生活,但没必要选择去做一名职业程序员。
现在很多编程培训机构,要么把编程作为一种技能来传授,要么当作竞赛升学的通道,从小学就开始教python、c + + 之类的语言。而我觉得:普通人最应该学习的不是一门编程语言,而是编程的基本概念和解决问题的方法论。
编程的每一个核心概念和方法论都是一种思维工具,可以迁移到任何领域重复使用,并受益终身。不仅每一个孩子都应该学,每一位成人也都应该学。于是我又开始筹备一门面向成人的编程思维启蒙课,还有一本入门级科普书《编程原来是这么回事》……
后话
我认为理想的编程教育,目标应该是最终能培养出这样的人:
能系统性整理自己的逻辑,并有条理、无岐义地高效表达自己的想法;
能把一个复杂问题分解成简单的子问题,并分别解决;
能有效运用「试错、反思、迭代」循环,把失败与错误转化为成长的机会;
能认识到很多事不需要亲自去做,而是可以描述清楚之后交给别人去做;
能识别出哪些工作更适合由机器代替人来完成,并找出有效的解决方案;
能在不同的解决方案之间进行权衡,并找出最适合自己的那一种;
能理解日常所用软件和工具的基本原理,有能力判断它们能干什么、不能干什么;
……
一个普遍存在的需求,往往早就已经有了不少成熟的解决方案。我们在解决问题时,往往只是需要去寻找它们,并加以评估和选择。只有在情况十分特殊、所有现存解决方案都不适用时,再选择自己去创造。我们也未必需要亲手去创造,只要能把事情想清楚、也能讲清楚,和已经有创造能力的人合作就是了。
大方向已经有了,接下来就是无休止地实践、试错、反思和改进。要做的事情太多,而时间总是太少,但这就是现实。如何调配自己有限的时间和精力,使输出的价值最大化,这是值得所有人终生思考的问题。
我将继续努力前行,不教光阴虚度。感谢这一路上支持过我的人,有你们真好!