如何学习编程

一些资源

学习路线的重要性

没打好基础,学习时容易云里雾里、一知半解,功倍事半。

就好像如果理解了调用约定和参数传递的实现,那么有些代码的简化、栈空间的变化等就好理解很多。

如果学习中遇到不懂的概念就去查阅也是极好的,这样知道这个知识为何有用,反复遇到的知识也说明这个知识相对比较有用。

每个知识都查阅容易疲于奔命,理解消化查阅到的知识也可能 distract 或至少影响先尽快建立大体知识体系的进度,但惰于查阅则万万不可。

但如果一上来就是在啃枯燥高深的理论,不知道这知识有何应用有何价值,又容易丧失学习的兴趣以及动力。

于是乎学习的线路就很重要,应寻求前辈的建议,少走些弯路。

多阅读

多阅读别人的代码。

编程其实也 和写作相似 。写作需要多学习别人的文章,编程也要多阅读优秀的代码--他们的架构、他们的规范、他们的技巧。

也可以上 Twitch 平台观看大牛直播编程。

复习 Revising

  • Can you solve it by yourself, after reading the solution?
  • Can you solve it in another (more efficiently) way?
  • Can you use this strategy to solve another problem?

不要对记忆抱有偏见

学习编程其实和学习基础数学很像,如果不想成为脚本小子 script kids,不想完全离不开 google 和 stackoverflow,(别误会! google 和 stackoverflow 很重要,但你不应该过分依赖于它们而荒废了自己的思考)那么就是知识点的记忆以及分析解决问题的能力。

之所以说到基础数学,是因为有些数学,尤其是那些高深猜想的论证,确实不是那么直接,而比较高深,甚至拐弯抹角依赖一些精辟的技巧(奇技淫巧)。

那么让我们回忆一下,我们如何学习数学。我们学习他人的解法,观察相似之处(条件或者目标),并尝试类比,模仿添加辅助元素的办法,在自己的解题过程中加以应用。

所以不要害怕一开始接触编程想不出来,不要害怕去阅读理解甚至记忆别人的代码,不要什么东西都想着要自己想出来,对于一门新的东西,尤其是像函数式语言这种思维方式和传统编程语言不一样的语言,更应该努力学习甚至记忆别人怎么实现,这样才能比较好地在自己解题过程中进行类比。

再一次,不要误会,我不是在鼓吹机械地记忆:我们应该理解别人的思路,并适当的记忆。 这里的记忆指的是消化、理解总结变成自己的东西,为什么这么编,需要用到什么,思路是怎么样的,遇到同样或者相似的题目自己可以编的出来。毕竟所谓学习,不应该在脑袋里面什么知识点都不留。

事实上,一些比较有用的函数,还是应该有个印象。又比如模板的使用、异步同步的处理方式、回调的使用、闭包的使用、经典架构……学习编程,完全脱离记忆其实并不靠谱。

应用很重要

接下来最重要的就是去应用,这样才能确保巩固自己的能力。用自己学到的东西,当然其中你往往会遇到别的更多的问题,在解决他们的过程中要能进一步提高自己。

初学者经常会被一句话毒害:

“不要重复造轮子。”

但是事实上,这句话的原话是:

“不要重复发明轮子。”

对于现成的工具,为了效率,不应该浪费时间重新从无构建。而应该合理利用,尤其是利用那些优秀的工具。

但是在提升自己的能力的过程中, 重复造轮子 还是很重要的。

不然要一个初学者突然想出一个又好玩又新颖的点子实在不太简单。何况既然是轮子是新的,初学者怎么知道能不能实现、自己水平是否足够?初学者可能会信心不足。

在这过程中,自己解决问题的能力得以锻炼,并且重复造轮子的过程中很重要的一点就是对比和学习别人的代码:别人的思路、别的架构、别人的风格。

除了重复造轮子,自己 实现自己的 idea 也有利于锻炼、检视自己的能力,而又是一个很有趣很有成就感的事情。

甚至可以在前面提到的 Twitch 上直播自己编程的过程,参见 我在 Twitch 平台直播编程的经验,这样的输出方式,促使自己认真准备,回答别人的问题也是一个查漏补缺自我交流提升的过程。

Code review checklist

project lead

  • 必须对系统设计了如指掌
    • 知道一个新功能, 需要现有框架如何改动,整体实现大概需要多少时间
    • 以作出正确的决定
  • 利用人力资源高质量完成项目
    • 定义项目,决定要怎么样做什么(包括架构、选型)
      • 选型, 评估一个框架
        • 对访问权限的统一控制
        • 自动测试的支持
        • 对 request 和 response 的 formatting,以及 serialization 和 deseialization 的支持
        • 对 logging 和 logging filtering 的支持
        • 对自动文档生成的支持
        • 实际实现的架构以及性能的考虑
      • 选型, 更清晰地考虑系统的设计,从而做出更好的选择( 拥抱约束 ). trade-off
        • CAP 只能三选二
        • 被牺牲掉的,就是它们的约束条件
          • 真正明白一个产品的内在约束条件,才能使你更好地考虑产品设计和工具的选择。
          • 否则在设计里打个补丁,继续前行,这样的补丁会越来越多. 越来越复杂的结构只是为了应对越来越多的 "exception",最终变成一个臃肿的夹杂着各种 corner case 的怪胎,从而不得不推倒重来。
        • hash-key 的限制条件严苛,使用不方便, dynamodb 让人各种施展不开?
          • 在什么场景下使用,是否真的必要,可否用其他技术?
          • 真的避无可避,而且是非常重要的功能,则考虑换用其他
    • 调动工程师潜力
      • 擅长什么
      • 积极性
    • 帮组员清路障
      • 提前清楚项目可能遇到的障碍
      • 外组间需要进行的沟通和协作
      • 技术难点需要寻求解决方案
      • 挡一些产品上的不合理要求
  • plan
    • 确定需求(功能)
    • 选型
    • 文档
      • api
      • db
    • 链路
    • task、人员、进度

architect

  • 技术债
    • the good, 适当引入技术债
      • 销售要某个产品和别人对标打单,市场要编制一个美丽的五彩缤纷的故事来应付发布,客户要求在限期之内完成某个他们自己也不知道什么时候才使用的功能
        • 完不成,产品卖不出去,市场推广不开,客户和你一拍两散
        • 这种时刻引入技术债是不得已的聪明的做法。
      • MVP, minimal viable product
        • examples
          • M$
            • basic
            • DOS
          • mongodb vs rethinkdb
            • mongodb
              • call me maybe mongodb
                • 一个数据库竟然靠 mmap 来提高效率,通过 fsync 来保证持久化(如果你不打开 oplog 的话,就只能听天由命了),然后还好意思发布 1.0 一路到 2.x
                • mongodb 在购买了 wiredtiger 引擎后,基本解决了最让人诟病的技术债,在软件服务的路上走得还算顺畅。
            • rethinkdb 错失了 NoSQL 的红利期,赚不到也筹不到足够的钱维持其运营,不得不解散团队
    • the bad
      • 针对其做了无数优化, 仍然挂/不稳定
      • 债务的叠加
      • backward compatibility
        • 接口设计上的缺陷被使用者当成了功能去使用,使用的范围之广以至于开发者在新版本中无法还债,只能被动地一路保有这样的债务
        • 对外的接口(API,SDK等)一定要小心设计
    • the TAO
      • 拥抱 MVP。先解决温饱问题,再考虑还债。
      • 把技术债外包出去 给更合适的团队
      • 雇佣你所能获得的最优秀的人
      • 拥抱匡威定律。组织架构决定了你的代码结构。
        • 快速交付 -> 拥有直接决策权的端到端的功能团队, 而不是开发,测试,运维等彼此独立
        • micro service -> 组织结构上就要打造彼此平行的 service team
      • 在实现上可以多些负债,在接口上尽量减少负债
      • 意识到软件本来就要不断修改、新增、删除
      • 健全的监控和测试