• Feeds

  • 程序员修炼之道-DRY与巧合编程

    DRY(Don’t Repeat Yourself)是架构设计中经常用到的一句话,这一原则应用非常广泛,在程序设计中同样会用到,不少代码或许不知不觉中违反了这一定义,如《程序员修炼之道》一书中就有如下一题,重构下面一段代码

    if (state == TEXAS) {
            rate = TX_RATE;
            amt = base * TX_RATE;
            calc = 2 * basis(amt) + extra(amt) * 1.05;
    } else if ((state == OHIO || (state == MAINE)) {
            rate = ((state == OHIO) ? OH_RATE : ME_RATE);
            amt = base * rate;
            calc = 2 * basis(amt) + extra(amt) * 1.05;
            if (state == OHIO)
                    points = 2;
    } else {
            rate = 1;
            amt = base;
            calc = 2 * basis(amt) + extra(amt) * 1.05;
    }

    可能很多人读此代码都有似曾相识之感,不错,我们身边不少程序员就是如此编程的。这段代码就是由于太多Repeat造成罗嗦难懂,结构复杂,维护困难。大家可能都会迅速想到两点重构方法
    1. amt, calc 可以移出来
    2. 第2个if可以拆分

    但是这样就完美了吗?4个if/else是否让人闻到一股不对劲的气味?这段程序是否还是传统结构化编程思维?if条件中state再增多程序会怎样?因此虽然是一段很短的代码,但是重构优化实际是无止境的。

    再谈巧合编程(Don’t Programmer by Coincidence),在很多项目中其实也很常见,巧合编程就是有问题的代码在开发过程中恰好能运行通过,但是运行在别的环境很容易就出问题,比如下面的C++代码

            a = 2;
            b = 3;
            if (a + b != 5) exit(1);

    什么情况会exit(1)?

    传统智慧认为,项目一旦进入编码阶段,工作主要就是机械的把设计转换成可执行语句。这种态度是许多程序低效、不可维护的最大原因。

    我们大多数人都能够几乎自动地驾驶汽车,我们不用明确的命令我们的脚踩刹车,或是命令手动方向盘,我们只是想“减速并右转”。但是,可靠的司机会不断查看周围的情况,检查潜在的问题,并且让自己在万一发生意外时处在有利的位置上。编码也是这样。

    因此开发过程质量问题非常重要, 有经验的程序员懂得如何避开前进过程中的各种雷区。Code review就是在你的驾驶过程中,由另外一名有经验的驾驶员坐在副驾的座位上,帮你纠正各种危险的驾驶习惯,避免在当时或以后踏入各种已知的雷区。

    本文读后感已先前在新浪微博发出,收到过几十条转发及评论。微博的内容或许更有灵感并及时,欢迎关注http://t.sina.com.cn/timyang

    做卓有成效的程序员

    最近阅读了《卓有成效的程序员》(The Productive Programmer) 一书,此书虽是2009年出版,但是介绍内容的价值并不会随着时间过去而降低,相信5-10年后对于大部分开发者仍然具有借鉴价值。

    大部分章节是介绍具体的方法来如何提高程序员工作效率。记住具体的技巧未必有太大价值,很多人都认同一种观点就是,读一本书最终的目标是忘记其中所有的观点,但是它会潜移默化影响了你以后的思维和或观点,包括你的行为。因此我认为此书直接的影响就是帮助思考开发过程的方法和问题,思考以前的惯例是否存在问题。比如最近在设计某个产品删除功能的时候,一位同事突然提到是否产品将来有需要查看行为历史,如果需要记录历史,删除的流程就会变复杂,不但影响删除功能的实现,还会影响后端数据的规模,从而进一步会影响架构的设计。因而我的直觉是这是一个过早优化,也就是书中第9章讲的YAGNT(You Ain’t Gonna Need It 你不需要这个特性)。由于第一时间的质疑,这个可能需要耗费大量开发时间的特性被暂停,对于项目本身或许是一件好事。

    此外书中一些敏捷的思路也会带来一些间接影响,我还意识到过去一些非敏捷方法可能会给项目带来风险,一个过去的项目,开发完成之后逐渐变得臃肿,和大部分后端服务相似,需要依赖一些特定的数据库及其他依赖环境。由于不希望开发环境与真实环境差距太大,开发环境也全部按真实环境最小单元的配置来进行,这就导致开发依赖的环境很多

    内部依赖
    MySQL master/slave
    分表
    Memcached(多个)

    外部依赖
    Message Queue(消息队列)
    用户及鉴权服务
    internal HTTP service

    导致的问题
    需要特定的环境才能开发,比如公司配好的环境中
    无法在家中或咖啡馆干活,因为系统跑不起来,无法看到效果
    一旦部分依赖环境有问题,则无法开工,只能等着服务恢复
    一旦几天没跟进代码,可能就由于环境设置的变化而无法独自启动工程。

    这些问题就导致代码改进的工作令人生畏,要像Google那样做到任何对代码感兴趣的人可以patch代码的意愿都会变成”mission impossible”。因此对于这个项目来说,最急需的一个改进是分析依赖,让项目能够随时随地可以方便的跑起来,大家可以很简单的改进代码,对改进的代码进行测试验证,测试之后新的代码基本可以无风险的运行到线上环境去。否则臃肿的项目只会降低大家的贡献的热情,最后变成一个死气沉沉的工作任务。

    你的项目中的惯例是否存在问题呢?比如Java中POJO中无用的get/set方法,比如一些使用c/c++来“优化”项目中的瓶颈而走向时间泥潭的经历,比如贵公司是否又在雄心勃勃要做一个自己的框架,事实上这个框架对于项目本身毫无价值。诸如此类的情况会非常多,这本书主要介绍的是程序员要如何提高自身的效率,但我觉得程序员更多的也应思考团队的效率改进并发出声音。

    构建可扩展的微博架构(qcon beijing 2010演讲)

    在使用Twitter几年的时间里面,经常思考微博如何更好的实现,恰好最近几个月也参与了相关工作,大部分都是工程实践,总结实践会促生更具实际价值的理论。因此在QCon Beijing 2010这次演讲参考了不少网友的意见后选择了《构建可扩展微博架构》的题目。
    由于在决定选题时知道来自Twitter总部有30万followers的@nk也会讲一个类似的题目,心中当时有点忐忑,最大的顾虑就是要讲的领域更他重叠,如果他讲得更深入,我就没必要班门弄斧了。后来考虑到以下几个原因还是决定继续

    • Twitter架构是单IDC设计,从它递增的tweet id就可以看出,后来当面向@nk提问也得到了证实。
    • 中美网络环境差异,单IDC和多IDC有很多设计上的不同
    • 大部分参会人员未必能对英文演讲有深入理解及感悟,中文的演讲可以讲一些细节解释更透彻。
    • Twitter对故障的容忍度大,国内公司对服务故障通常更敏感。因此国内架构师会考虑设计方案尽量简单可靠,服务需要更稳定。国外开发团队更倾向追求在工作中应用技术创新,因此会导致架构设计理念的不少差异。

    演讲的slide如下,登录slideshare之后可以下载。

    这里再补充在qcon演讲未来得及考虑成熟的一个方面,用户规模影响设计,具体是指用户数每上一个数量级,许多设计需要重新考虑。

    10万用户级别

    • 单服务器,前端、后端、cache、db在一起。

    百万级

    • db和cache单独部署服务器,db或按业务进行拆分(sharding)
    • cache或使用一致性hash扩展。
    • 前端后端还是在一起,但是根据业务拆分,每个业务可分配不同数量的服务器

    千万级

    • 开始重视架构设计,有专门技术架构师
    • 需跨机房部署,前端在远程增加反向代理加速,数据库在异地机房使用slave数据库副本
    • 后端拆分出来,系统内部需要远程调用,内部需远程调用协议。

    亿级

    • 架构更细分,或增加数据架构师,cache架构师,分布式架构师
    • 数据库sharding碰到烦恼,开始考虑分布式数据服务
    • 数据访问需要根据业务特点细分。
    • 开发、运维、测量、调优具备有自己的专有工具。
    • 所有服务需要地理多机房分布,具备IDC容灾设计。
    • 服务可降级

    上面的数字仅供理解“用户规模影响设计”,数字本身并无具体指导价值。

    另外在slide中也提到了,目前新浪微博团队急需人才,对上面相关技术领域感兴趣的架构师及各层次开发人员(熟悉PHP,Java, C或数据架构任意一种)可随时跟我联系,工作地点为北京,联系方式见博客首页。