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++的时候,写个冒泡排序都把我憋出大红脸出来,后来接触到函数式语言才发现,原来编程是可以这么有趣,这么爽! 迷途的人们啊,赶快投入到函数式语言的怀抱中来吧 :)

看了陈金洲写的≪架构腐化之谜≫,深有感触,这篇文章几乎精确地描绘了国内软件行业大多数公司的现状,我对于目前所在的一个项目从效率方面进行了分析,主要发现有以下几点问题:

1 架构失控。

到处充斥着代码泥团。

        2 功能杂糅。

为了应对各种多变的需求,新增各式各样的开关配置项,代码中增加对这些配置项的判断逻辑,增加了代码的复杂度。

3 build, deploy 时间过长。

目前我们构建一个发布包需要30分钟。

4 启动时间过长。

我测试了一下,在一台 CPU 为core dual 2.0GHz、 4G 内存的机器上,启动UT容器需要40~50秒,启动web容器需要4~5分钟。容器启动越慢,那么开发人员得到的反馈周期也越长,试想在一个几十个开发人员的项目组里出现这种情况,这是多大的浪费!

对于上面四个问题,我有些自己的想法想跟大家分享下:

1 实施code review。

开发过程中必须进行 code review,如果条件允许,可以尝试pair programming, 面对面的交流永远是知识传承的最佳方式。

通过新老员工之间的code review 或者pair programming, 新员工可以以最快的速度了解项目内部一些约定:如,展示层的公共逻辑应该放在ViewCommon工程里;除了工具类,应该尽量避免静态方法调用;也可以尽快了解他应该写什么样的代码,写什么样的代码会挨批。

只有所有人都去维护这个架构,遵守约定,那这个架构才能发展下去。好的架构不是设计出来的,而是演进出来的。

         2 有必要做这么大吗?

对于第二个问题,我有个疑问:我们做了那么多功能,客户现在用到了多少?做了功能然后又关掉,然后等后面客户要的时候再开启,那么在这个功能已经实现到这个功能被开启正式商用这段时间,这些代码的维护成本谁来承担?这些是不是浪费?

即使客户的需求真的有这么多,他的商用计划是什么?是不是所有特性在这个版本发布时会全部上线?等考虑完这些问题,那么这些所谓的开关项,以及由这些开关项所控制的功能组合而成的大项目(这里的大指的是在物理部署上大)存在的必要性就值得怀疑了。我们不妨换个思路:把产生这个大项目的大需求划分开来,把大项目分解为几个可以独立部署、运行的小项目,然后分阶段实现这些项目,逐步提供给客户,这样的客户感知就是系统主页上不断新增的链接或者tab,而实际上这每个链接或者tab后面都是一个新部署上线的子系统。而这些子系统的开发、维护成本都肯定低于一群人在一个大项目里捣鼓。

         3 精简build 脚本。

把构建任务中的每一步都独立成一个单独的target, 即能达到脚本重用的目的,又能让开发人员灵活选择自己想要运行的target ,另,目前我们的项目还是存在循环依赖的情形,这个问题必须解决。

 4 这个问题有多种解决方案:

 1)给开发人员换更好的机器

 2)写UT来验证功能的正确性,而不是

“修改代码->重启容器->刷新页面->查看结果” 这样一个反馈周期超长的流程。

 3)写UT用例尽量不要启动spring容器,实际上绝大多数应用逻辑不需要启动容器就可以进行测试。

     什么?你问我们那些公共能力怎么办?比如数据字典,比如配置项列表?我建议你用stub吧,这个可以节省很多时间,写个桩是值得的。