好的编程习惯

 

昨天郑大晔校的主题是 “如何养成好的编程习惯”,只有养成好的编程习惯,当你在时间紧任务重的情况下仍然能够写出风格、质量良好的代码。

分组讨论后,每个小组都给出了自己认为好的编程实践,我把大家的想法总结了一下,并且根据这些实践的侧重点,分成如下五个方面:

任务规划:  Tasking,TimeBox

     关于Tasking,有两个大家最关注的问题

          ——如何确定tasking的粒度 ?

          结论:task的粒度是由开发者的能力确定的,

                   基本的评判标准是:如果你能够用一个测试用例描述这个task即将实现的功能,那么这个task的粒度就是合适的。

         ——如何划分task?

          结论:不需要一次性把所有task划分清楚,可以从最简单的task入手,在实现简单task的过程中,你会逐渐识别出新的task,

          把这些新的task加入task list中,待第一个task 完成后,再从task list中获取下一个task,如此持续下去直到所有需求都完成。

          推荐阅读《Test Driven Development》这本书,书中很好地演示了“Tasking”的实践。

     关于TimeBox,有同事推荐了一个比较有名的实践 tomato time,以15~30分钟为一个tomato time,有计划,有节奏地工作。

软件设计:  简单设计,先实现再重构,小步前进

     简单设计: 在软件设计上,一个程序员最容易犯两个错误——没有设计过度设计,两个极端。

                    对于一个刚入行的程序员,他/她最容易犯的错误是没有设计,比如,在一个类、一个方法内完成所有的功能。

                    对于一个刚入行并且阅读过设计模式相关书籍的程序员,他/她很容易犯一个错误——过度设计。比起没有设计,程序员更加难以意识到自己已经过度设计了。关于过度设计我有个深刻的体会,这里拿出来跟大家分享一下:

          在某个产品中,为了实现一个从远程部件获取数据,我想当然地为了提高效率,对从远端获取的数据进行了缓存,并且,为了进一步提高效率,在程序内部多个地方做了数据缓存,这种设计导致的问题是,在程序发布之后,当发现获取的数据是错误的时候,花了大量时间来定位错误的数据出现在了哪个环节。痛定思痛之后,我移除了这些缓存,把处理这些业务数据的spring bean改成无状态的,这样业务逻辑大大     简化,而且也没有发现性能低下的问题。 由此看来,这些缓存就是过度设计的思想引入的。

 

     先实现在重构:这个行为就是建立在简单设计的理念上的,其目的就是为了避免过度设计,完成刚刚好的功能。

效率提升:  熟练使用类库,熟悉IDE快捷键

     关于这两点,只有通过自己的实践慢慢积累,熟能生巧啦。

 

工程实践:  频繁提交, push前完成本地构建 ,  和pair及时沟通  

     频繁提交:频繁提交的目的在于及时地把自己开发出来的代码提交到代码仓库上去进行持续集成,验证自己的代码是否破坏了原有的功能。其本质思想在于快速得到反馈。

 

     push代码到代码仓库前完成本地构建:这个工程实践的目的在于,提前发现可能引入持续集成失败的问题,降低修复持续集成失败的成本。

     和pair及时沟通:在ThoughtWorks工作,大家都是以pair的形式进行的,这个实践的目的在于,及时从pair处得到反馈并获得成长。

 

代码风格:  代码要是自注释的,格式规范, 不提倡(switch, if else, 深度嵌套, 大函数,重复)

     关于代码风格方面,大家的观点基本是一致的:代码是给机器写的,但是是给人看的,所以要写人类能够看得懂的代码。

 

ThoughtWorks入职两周

 

从8月11日入职,到今天8月24,刚好两周时间,感觉又到了做总结的时候了。

     本周的主要工作是和一个今年刚毕业的研究生(在ThoughtWorks很多时候都是pair干活的)一起协助北京的一个同事制作一个关于Continuous Delivery的 demo,这个demo的目标是用于演示如何在实际项目运作过程进行Continuous IntegrationDevOps实践,达成Delivery的目的。

     经过简单的商议,我们选用了一个由我所在的“酱油组”维护的rails应用做为这些实践的应用目标。再把上面的大目标进行逐步细分,得到下面几个子任务:

     1. 搭建CI环境(这里采用的公司开发的一个CI产品 Go

     2. 把次rails程序自动部署到CI上

     3. 增加静态检查CI任务

     4. 增加unit test CI任务

     5. 增加functional test CI任务

     6. 增加产品 release CI任务

     7. 一键式完成产品部署(DevOps)

     其中主要涉及了rails, rvm, shell, DevOps等知识点,所有的这一切对我而言都是全新的,我们都是在未知领域里探索,在实现这些任务的过程中,我明显感到了我存在的一个问题,一个非常严重的问题——解决问题的能力较弱而且缺乏进一步钻研的欲望

     当遇到一个问题时,我在进行了有限的几次尝试后就放弃了,就准备向周围的人 请教了。

    其实ruby、rails、rvm、DevOps的在线文档都很全面的,多翻翻文档一般都能找到解决思路,更重要是,对于大多数问题来说,google一下,一般情况下你都能找到答案。

     在之前很长的一段时间里,由于始终在自己的一亩三分地里耕耘,我已经很久没有去面对全新的知识领域了,所以我几乎丢掉了原有的在未知领域钻研的能力。

     突然来到这个崭新的环境,海量的信息暴风雨般地扑面而来,太多的未知的东西需要了解,我感到了压力。

     好在,这个环境是开放的,资源很丰富,是应该利用好这些资源,给自己再来一次提升了,我要努力。

 

      检讨完自己的不足,也该总结一些优秀实践了。

     1. 构建思维导图

         在这两周时间的学习过程中,我都会打开XMind,逐步构建自己的知识地图,当读完一篇文档,一个简单明了的知识结构图有诞生了,让我对刚刚学到的知识有个系统的总结。

     2. 做笔记:

在阅读各种各样的文档中,我都会用evernote把自己的心得记下来,可以用来做回顾,同时这些材料都是写博客的最生动的素材。

 

This blog is talking about how I install metric_fu in a rails project.

firstly, I typed a command "gem install metric_fu", unfortunatlly, I got a error message like this

"ERROR:  While executing gem ... (Gem::DependencyError)

 Unable to resolve dependencies: metric_fu requires chronic (~> 0.3.0)"

seems that metric_fu can not find the required verion of chronic gem, then I googled chronic, and know that chronic is time parsing tool writen in ruby, the latest version of chronic is 0.6.2. 

so how should I solve this problem?

as I know, a gem package is just like a jar file, which contains  class files and some manifests files. but a gem package can do one more thing - telling you the name and version of those gems it depends on.  so I got a soluation :  change the version declaration of chronic in the "dependency declaration file", telling metric_fu to use the latest verion of chronic.

        Then I  googled again, and a got a import information - people can install gem from source , 

here is the steps:

1. download the correct version of gem source code from  code base (github in general)

        2. build a gem package from the source. 

        3. install the gem package you built.

for more detail, you can see here .

Note : step 2 is the key point.

        before I build my metric_fu gem, I check a file named

        "metric_fu.gemspec", and found out that this is the "dependency description file"


         then I find the dependency description of chronic "s.add_dependency("chronic", [">= 0.3.0"])"

modify the version to "0.6.2", run command "gem build metric_fu.gemspec",  so I got a metric_fu gem depends on chronic 0.6.2.

         finaly, cd to my project directory,

         run command "gem install ../gem_repositry/metric_fu.gem", 

this time, the metric_fu is installed successfully.

         Hope this information is helpful for you.

 

Update: this is not a good pratice.

                 you can install a old version of chronic manually, and then install metric_fu 2.1.1.

                and I met another problem when running metric_fu in ruby 1.9.2 with syntax 1.0 - "invalid UTF8 charater sequence", you can find the issue here .

                my soluation is just like Jscruggs said . here is my configuration code:

MetricFu::Configuration.run do |config| config.syntax_highlighting = false end

在回忆昨天在和同事们讨论一个需求的情景,总结了以下几点:

  1. 要理性地说服别人,切忌带有情绪沟通,保持冷静
  2. 用通俗易懂的例子告诉对方那样做有什么样的好处和坏处
  3. 不要试图通过展示自己的经验、资历来使别人认同自己
  4. 给出你认为更好的建议
  5. 做任何一件事情之前,首先考虑做这个事情是否有价值,是否有其他更简单的思路来获得客户的认可。

可参见dreamhead的这篇文章

Scala实现的各种排序算法

收到TW的offer已经一周了,已经跟现在的公司提离职了,于是整个人都松弛下来了,闲来无事,把自己在学习scala期间写的几个排序的小例子拿出来分享一下:

Insertion Sort:

def isort(xs: List[Int]): List[Int] = {
  xs match {
    case List() => List()
    case x :: xs1 => insert(x, isort(xs1))
  }
}
def insert(x: Int, xs: List[Int]): List[Int] = {
  xs match {
    case List() => List(x)
    case y :: ys => if (x <= y) x :: xs else y :: insert(x, ys)
  }
}

测试代码:

def main(args: Array[String])
{
  val xs = List(4, 6, 7, 3, 223, 999999, 6)
  print(isort(xs))
}

Quick sort:

def quicksort[T](less: (T, T) => Boolean)(xs: List[T]):List[T]= xs match
{
  case List() => List()
  case y::ys => 
    quicksort(less)(ys.filter( a => less(a, y)))::: List(y) ::: quicksort(less)(ys.filter( b => !less(b, y)))
}

测试代码:

def main(args : Array[String])
{
  val xs = List(4, 5, 8, 1, 10);
  val sorted = quicksort((x: Int, y: Int) => x > y)(xs);
  print (sorted)
}

不知各位看完后是什么感觉?想想用C、Java语言实现一个insertion sort、 quick sort 的代码量吧,再看看这个scala实现,无需关注变量状态,只需要告诉编译器,先做什么,再做什么,其余的事情就由编译器自己实现了, 现在应该了解到函数式语言的威力了吧。个人感觉函数式语言更加接近人类的思维模式,想当年刚开始学习C++的时候,写个冒泡排序都把我憋出大红脸出来,后来接触到函数式语言才发现,原来编程是可以这么有趣,这么爽! 迷途的人们啊,赶快投入到函数式语言的怀抱中来吧 :)