程序员的点滴回忆

在ThoughtWorks就要两年了,相比进来之前的自己,现在做事可以抱有更低调的态度了。因为当你和众多聪明的人做事情的时候,你不得不谦虚,逐渐的你就会形成“开放性格”,这是我从ThoughtWorks收获的最有价值的东西。

今天反思一下自己在开发中遇到的一些问题和TW的答案(只是一个Check list,没有逻辑顺序),有如下地方需要考虑:

  1. 做一个需求(对于我们指User Story或Tech Task)的时候要做好评估。评估一般都不准,因为预测本身就不容易准确。有几个方法帮助你让它变得准确,最重要的就是在你工作的团队里面计算Velocity,通过不断的评估Story point(实施工作量的一个概要评估,可以使用1、2、4、8、16这样的大概数字,和人天没有关系)记录实际完成时间,两者相除则为Velocity。有了Velocity就可以作为之后该团队评估需求实现所需时间的参考。Story point使用民主投票的方式,民主要做好就要保护好“异见”,任何高估和低估的情况都要平等的讨论清楚,并达成一致。
    上面说的方法在某个需求有未知技术点的时候会产生严重的评估偏差,甚至完全无法评估。此时一般使用的方法是Spike。Spike的做法是从技术可行性着手,多参考网上的资料和现成代码,使用Quick and Dirty的方式让他们工作,并且在这个过程中评经验得出实现它大概付出的代价,帮助你正确评估一个需求。Spike要尽量覆盖自己所有拿不准的技术点,不要深度,但是要有足够的广度。Spike代码可以不写测试,但是推荐使用测试的方式去实验(因为它更容易组织)。Spike代码一样需要使用源代码控制,并且要多做提交。最后,最重要的一点是Spike代码永远不要进入production code base。李晓曾经说理想的Spike方式是做一个基本满足需求的Spike原型,然后抛弃,重新用TDD的方式实现相同的需求,保证进入code base的代码都是TDD出来(或者起码是有测试保证)的。
  2. 关于些代码是否要测试的问题,Kent beck说我只给会被Break的逻辑写测试。但是实际上不仅如此,在Kent beck总结出的TDD中,测试还是用来明确你的“意图”的代码。做事要有明确的“意图”,一方面它保证你不会迷失目标,另一方面它也是你和你的代码还有你的同事沟通(还包括你自己)的重要手段。但是这里有一个容易让人迷惑的事情,TDD出来的测试到底是测试分类(单元、集成、验收)中的哪一种呢?同事们的答案是它们没有直接关系。TDD是可以表达你对代码行为/结果“期望”的任何一种测试,当然你要尽量保证你的测试是更靠下面的测试(单元测试),因为底层的测试运行快依赖少,所以保证你的红-绿节奏更快。冤有头债有主,每个测试表明一个“意图”,几个“意图”对应一个需求,一个需求对应一些商业价值。如此的映射帮助你从代码的“意图”到“价值”打通经脉。
  3. 一般说User story以“As someone, I want to do something, Then I can achieve some business goal”的形式书写。一般说与之对应的验收条件以“Given some context,When something happened, Then you got the result”的方式去组织,这种书写方式可以保证你的验收条件被映射到一些Specs上面,一些逻辑相关的Specs就组成了对一个Feature的验证。如果你用这种方式组织你的测试,那么你就自然的实践了BDD。BDD是一个很大的概念,组只好测试只是个开始。Ruby里面的Cucumber是一个很好的验收测试工具,它能帮助你更好的从你使用需求及验收特性Driven你的测试,书写有价值的代码。而Rspec这样的BDD测试框架则可以帮助对你的其它测试(如单元测试)进行管理。这些都是形式,但是形式影响你思考的方式,最终可以帮助你提升的代码的“价值”量,减少“无价值”的代码。
  4. 如果你是一个测试驱动狂人,你是否要考虑试验ATDD(验收测试驱动)呢?我们认为ATDD是TDD的一个极致形式(因为它最大程度保证你的商业意图和代码的有效映射)。但是这里还是有很高的门槛。一个是ATDD的难度的确比其它层次的TDD要高,很多时候你都苦恼于不知道如何做Assertion,这种问题会影响效率。因为测试代码本身是没有商业价值的。另一方面即使你是脑力超人,可以很好的写出验收测试级别的测试代码作为驱动,这些代码运行的速度一般也比较令人失望。因为根据测试分组(Category)的理论,越在上层的测试运行速度越慢。所以,如果你用验收测试作为高层驱动可能会拖慢红-绿节奏。这时你需要清除的知道要向下面层次的TDD进行委托,保证节奏效率的同时还能清晰的让自己的测试回溯到“商业价值”上面。
  5. 保持一致的标准。有的时候你会说我们的Code base不允许Quick and Dirty代码的污染,有的时候你又说我们的首要任务是让它Work。Teck lead此时要保证好统一的标准,找到自己团队能够接受的合理平衡。
  6. 站立会议一定要短,昨天作了啥,有什么问题,今天准备做啥这个形式如果哪个方面与团队没什么关系就可以省略。站立会议不是报功会,所以不要说你Fix的bug的编号是什么,那个完全没有价值。
  7. 回顾会议非常重要,如果大家都觉得需要回顾会议的时候就开。时间可以是几个小时一次也可以是几个星期一次。常用的Theme是“Well/Less well/Puzzle”,回顾会议需要Time box,一般半小时以内比较好。长时间跨度的回顾可是使用Timeline的Theme,就是大家不根据资料回忆一个时间段内发生的印象深刻的事情(团队和项目相关的)和记忆中他们发生的时间,然后大家讨论这个事情产生的正向/负向的效果,因为记忆会“骗人”,所以收集到的记忆的偏差实际上都可以和当时团队的情绪变化产生映射(类似精神分析法,可以帮助团队找到很多有意思的东西)。回顾产生的偏差是个很有趣的话题,这是为什么我们办公室有大量的精神病和精神分析相关书籍的原因。其它的Theme还包括Future retrospective,就是回顾“没有发生的未来”的回顾会议,使用“Well/Less well/Puzzle”作为容器,收集你想要得到的和希望避免的事情。出了这些基本形式,我们的Bonna还发明过大字报方式(就是搜集Headline hot news,bad news,流言八卦),用来收集各种客观消息并且集体对齐进行评估。Star fish也是一个Theme,不过因为大家总是忘记它的缩写是什么意思,用的比较少。回顾会议的主持人非常重要!还有回顾会议归档以后所有Action item都需要有负责人,并且要定期验证结果。
  8. 结对的时候两个人产生分歧是正常的,争论也是好的。不过不要争论太久(如超过15分钟)。久的争论大部分的原因就是互相没有聆听,产生了共同障碍。此时解决方法就是暂停争论,按照听起来比较短的路径前行一段实验一下,然后再讨论。此时一般会发现殊路同归,或者发现死胡同以后可以早点掉头。
  9. 只结对解决重点问题和难点问题是可以的,但是大部分的问题是你根本不知道哪里是“难点”和“重点”。所以资源和体力允许的时候尽量结对解决问题。
  10. 评价一段代码的好坏:首先看是否容易读懂,意图是否明确。这个要求需要你无惊讶的设计(使用模式、精心的设计)还有make sense的方法/变量命名。其次代码/方法要短,代码短是硬道理。然后是是否有代码重复。都达到了以后再看效率等问题(效率关键场合除外,但是这种场合很少)。
  11. 做事要尽量自动化,DRY。

困了,下次继续。

再谈网页设计中的em与px

回应hax大牛在“em or px“的讨论:

上次看到你在我分享的friendfeed上面的评论我就想好好讨论一下这个问题了。’em’是从字体设计上面继承的一个概念,是指字模的高度,一般书上会说em能够保证不同字体的大写X的高度保持一致。那么大写X的高度就是这个字体的em。

你说的px是相对单位不仅在你说的显示器尺寸上是相对的,而且由于大部分的矢量元素的渲染来说比较先进的浏览器都支持“半像素平滑”,连像素内的颜色的投影也可以是相对的(渲染引擎会进行像素平滑的计算)。

在网页渲染的布局上来说它要转化em到px这个单位,就是说在一定的zoom下两者是等价的。

但是为什么我还是很支持使用em作为首选的字体和布局单位呢?当然这不是用来保证一行到底可以显示多少文字,因为px也可以保证。主要目的是区分布局中“缩放友好”的矢量元素(如字体,布局相关的所有html元素)和“缩放不友好”的标量元素(如很多“替换元素”是不友好的,典型的如图片,还有在使用某些Windows主题以后的input元素)。在使用webkit的浏览器,缩放会更加友好,文字和布局会随缩放变化,但是图片等却保持原有的最佳尺寸。Firefox3以后所使用的全部缩放我觉得在有些时候是有用的,可是总有它让你不爽的地方⋯⋯

比如如果你在某些设计场合希望容器变大文字尺寸不变,从而在缩放显示更多内容。另外一些地方你希望容器和文字尺寸一起变化。px和em的配合让两种需求可以比较容易的在同一个页面出现。

从设计为内容服务的角度考虑,使用em作为基本单位可以让你更集中于“内容驱动”,避免过分的”设计驱动“。我的工作范畴内前者更常见,也更符合客户的需求。

pac文件生成脚本

之前写过一个我的pac文件,但是非常不好意思。因为那个脚本我为了定义网站列表比较简单,用了个数组。可是pac文件并非在完备的javascript环境运行,所以动了手脚以后这个pac文件只在firefox里面可用,而safari和系统级别都不能用(MacOS X)。所以还是老实写传统的好。可是转念一想,不如还是ruby一下吧。这样修改起来还是加一个域名就OK了。

#!/usr/bin/env ruby
proxy = 'SOCKS 127.0.0.1:7777'
gfucked_sites = [
  'youtube.com',
  'ytimg.com',
  'googlevideo.com',
  'tinyurl.com',
  'bit.ly',
  'blogspot.com',
  'twitter.com',
  'plurk.com',
  'torproject.org',
  'wikipedia.org',
  'tumblr.com',
  'soup.io',
  'pandora.com',
  'adobe.com',
  'last.fm',
  'photoshop.com',
  'google.com',
  'googlecode.com',
  'appspot.com',
  'acer.com',
  'demonoid.com',
  'alexa.com',
  'wikimedia.org',
  'yahoo.com',
  'zend.com',
  'aptana.com',
  'uncyclopedia.tw',
  'uncyc.org',
  'webarchive.org',
  'pornhub.com',
  'yourporn.com',
  'facebook.com',
  'mail-archive.com',
  'versionapp.com',
  'friendfeed.com',
  'ff.im',
  'chinagfw.org',
  'bullogger.com',
  'mulhollanddrive.com',
  'iphone-dev.org',
  'plurk.com',
  'imageshack.us',
  'zendesk.com'
]
puts "function FindProxyForURL(url, host) {"
gfucked_sites.each do |site|
  puts "  if (shExpMatch(url, \"*://*.#{site}/*\")) { return \"#{proxy}\"; }"
  puts "  if (shExpMatch(url, \"*://#{site}/*\")) { return \"#{proxy}\"; }"
end
puts "  return \"DIRECT\""
puts "}"

如果使用tor则修改proxy=’SOCKS 127.0.0.1:9050’就OK了。生成一个放在可以访问的Url上,然后让整个系统使用这个proxy,我们就又可以使用我们喜欢的twitter客户端了(for me it’s tweetie)。

Javascript库的“实现模式编程(Programming to patterns)”

今天看到了一篇很好的讲Javascript Library的文章:http://www.clientcide.com/best-practices/jquery-and-the-ajax-experience-programming-to-the-pattern-and-what-really-makes-one-framework-different-from-another/
作者是一位MooTools开发者,他写了对实现模式编程(programming to the patterns)进行了阐述。告诉我们像Prototype、MooTools这样模拟类继承并鼓励使用OO的方式组织你的代码对于你的好处。而对比的对象就是红火的JQuery。他的观点我举双手双脚同意。

JS的通用库最基本的目的是给你解决浏览器兼容性差异,让你在与浏览器兼容性的战争中给你一个巨大的后援保证。但是只提供浏览器兼容性的JS库是不会流行的,你还需要在编程模式和代码效率(Code efficiency)上提供帮助。对于我个人来讲,对语言的流畅性改造最好的就是Prototype这个库,因为它营造了一个“最小惊讶”的Ruby语言环境,让你几乎感觉到是在编写流畅的Ruby,尤其在Rails项目里面使用更让你感觉到Ruby和Javascript结合的流畅。(此处我想到了上周Cat Chen同学和我提起的“Language Oriented Programming”的概念,也许这就是它的表现吧,扩展语言减小编程中的“惊讶”)相比之下JQuery则在代码效率上做了很大功夫,这个代码效率不是指运行速度,而是单位代码完成的功能。JQuery对Dom的DSL化封装,还有对method chain的大量使用,几乎让你感觉在声明行为,所以它让非常多的对啰嗦的Dom编程厌烦的前端程序员迅速“上瘾”。但是,我们知道DSL化的JQuery还不够,因为它很好的解决了可读,但是并不一定容易维护(尤其是过度使用method chaning)。另外一个原因就是习惯使用“声明化”以后会让一些程序员忘记“抽象”和“封装”,很多JQuery都没有使用面向对象OO的方式去组织自己的代码,而是拥抱了面向过程编程,而很多人却还自我安慰说自己使用的是“函数式编程”。如果你没有做好函数抽象,那么绝大多数情况下你的JQuery code都不符合函数式编程的本意,此时你也就离抽象出可复用的“模式”越来越远了。

其实JQuery的很多UI plugins是很好的例子,尤其是支持Progressive Enhancement的JQuery UI插件,它们都很好的规定了自己的micro formats,然后根据dom的模式来组织自己的行为模式(Behavior and Patterns)。它们才是很好的对实现模式编程(programming to the patterns)的例子。缺少了对模式的抽象也就失去了在大的项目中实现代码效率(Code efficiency)的优势(因为代码复用度会下降),这对于JQuery这样一个以“Write less, Do more”为口号的库来说就失去了它最大的卖点。

所以,实际上关键的问题就是,用哪个JS库都没错,但是如何使用和形成模式才是关键。每个库组织自己的“模式化代码”的风格不一样。但是像MooTools和Prototype(还有YUI)这类基于类去组织代码的库的源代码本身就已经告诉了你如何组织你的代码,而且它们的OO实现对于任何一个熟悉OO的程序员来说都“没有惊讶”,那么你就很容易吸取他们所推荐的代码组织风格了。

最开始所引用的文章归纳了几个不面向“模式”组织代码的坏处:

  • 缺少抽象,缺少复用
  • 会形成零散的代码,修改起来成本会比较高,维护成本也就增高了
  • 不OO,不容易组织扩展

我补充一点:

  • 过程化的代码不容易测试,不使用模式的代码也会造成测试的零散,造成浪费

更新:有一点不准确,就是关于复用,其实Javascript的各种Widget库(如ExtJS、Dojo和YUI的widget库)都做到更好的复用。缺点是目前的widget库中的高级控件都严重的绑架了Dom结构,造成自己修改Dom结构比较麻烦。而没有使用Micro Formats这样的基于标准的弱耦合,这是一个很大的问题。所以由于这样的实现绑架的问题,这些Widget库不包括在上面所述的框架对比中。

解决Mac环境下的Java Version问题

前一段时间遇到了一个奇怪的问题:我的Mac下面的Lotus Notes 8.5和FreeMind 0.9rc4都不能工作了,它们的共同特点就是都基于Java。我试验了在Terminal里面启动/Applications/Notes/Contents/MacOS/Notes的话是可以启动的,但是会启动以前的Basic界面(后来我才知道可能它是个Cabon应用,而不是Java版本)。所以就一直凑合着用。
可是昨天我尝试运行/Applications/Utilities/Java Preference的时候它也无法启动了,我这才觉得这不是个简单问题,必须要彻底解决。

我回忆了一下,我最开始的时候尝试用Java Preference的时候修改了JDK的顺序,将JDK6放在最上方,可是在Terminal里面(java -version)依然只可以访问1.5。所以当时就手动修改了系统的指向:


/System/Library/Frameworks/JavaVM.frameworks/Versions/Current -> /System/Library/Frameworks/JavaVM.frameworks/Versions/1.6
/System/Library/Frameworks/JavaVM.frameworks/Versions/CurrentJDK -> /System/Library/Frameworks/JavaVM.frameworks/Versions/1.6

这样修改以后就工作了,命令行里面执行java -version显示:

java version "1.6.0_13"
Java(TM) SE Runtime Environment (build 1.6.0_13-b03-211)
Java HotSpot(TM) 64-Bit Server VM (build 11.3-b02-83, mixed mode)

但是大家千万不要这样做,这个其实是后面很多问题的一个重要原因。

如果你修改了上面的指向,我推荐你unlink它们,然后下载Java for Mac OSX 10.5 Update 4 (http://www.apple.com/downloads/macosx/apple/application_updates/javaformacosx105update4.html)重新安装一下就可以重新生成这些link了。不过记得要unlink自己手工建立的符号链接(比如我的是Current, CurrentJDK, 1.6),否则升级包无法替换你建立的那些。

但是我这样做了以后还是没有解决所有问题,Java Preference可以正常启动了,不过它设置的顺序依然不管用,java -version总是返回1.5。我突然想到估计是运行我们的Ant脚本曾经提醒过JAVA_HOME的问题,我手动设置过一个,忘记去掉了。所以去查我的.profile,发现了罪魁祸首:

export JAVA_HOME='/Library/Java/Home'

这个设置是错误的,虽然它存在,可是它不会根据Java Preference修改的结果去寻找你的Java的bin和lib,也就造成了后面的一系列错误。

修改为(或者删掉JAVA_HOME声明也是安全的):
export JAVA_HOME='/System/Library/Frameworks/JavaVM.framework/Current/Home'

问题解决,教训是如果你手头有工具,应该学会使用它,如果没有耐心学习就错误的使用工具,一开始感觉能解决问题,可是后来它会造成越来越多的错误。那么决绝方案就是回到原点,恢复,学习,正确的使用工具。

什么是渐进是增强呢?让我们为开放标准的Web做出一份贡献吧!

今天面试前端开发,令我相当不满意。称自己是前端开发,5年以上的工作经验居然说不清楚em、pt、px的意义(是她自己提出了这些单位,简单的给我解释为相对与绝对,我问她区别就打马虎眼……),也完全说不清楚一个自适应两栏布局的实现思路。和我说了一堆交互设计的观点,我的感觉就是经验有一点,不过思路完全是混乱的,一点逻辑都没有。作交互的看看UCD的文章很好,但是如果仅仅限于凑热闹那么就没有意义了。好比一个注册设计,说了太多种不同的观点,但是她依然没有说出如果做一个设计的所以然。实际上作为交互设计显然需要根据需求和客户类型做设计,然后应该有思路,有感觉,做出初步的设计后要通过一些用户调研的手段来取得有效的反馈。这仅仅是交互设计或者开发工程师达到工匠要求的一个基础,要想进阶一些那需要很多的设计感觉的。所以,像我这样的傻傻的程序员出身的前端并不要求你马上让我看到你对设计的强悍感觉(sense),起码你让我知道你是一个有思路有方法的人吧。

今晚看到淘宝的小马(泽新)同学的一篇博文:http://ued.taobao.com/blog/2009/06/24/web_dev_improve
写的挺好,是在说我们国内一些前端开发的朋友还在讨论基本的问题,我认为犯这样的错误主要是对Web标准的认识还是太浅显了。上周末我在Beijing Open Party上面临时救场(因为这次话题意外的没有达到9个),我讲的是《什么是渐进增强/如何渐进增强》。这里面没有太多具体的技术,主要是强调HTTP作为Web的支撑,是如何演化出了HTML、CSS、Javascript这三大法宝的。HTTP解决了我们构建一个相互联系在一起的对资源的不同表现(Representation)进行沟通的网络(Web),它解决了我们如何查询和定位资源的不同表现(Restful),以及如何修改这些资源的操作方式。因为它是资源的不同表现,所以实际上它是具有逻辑的数据访问层的一种新的抽象(如数据库+存储过程),这个观点是徐昊同学提出(告诉我)的。如果加上语义网的逻辑推演能力,那么这个数据表现的网络就可以成为智能网络,也就是语义网的概念。也就是说HTTP实际上是一个身肩重任的技术。而在目前阶段,这些非智能的网络的资源表现已经足够被人类直接消费了,那么为了服务它,HTML、CSS、Javascript这些技术也就顺理成章的出现了。他们三剑客要解决的是关注点分离,让我们从数据结构(Representation)、表现、行为方向去帮助人类能够直接消费的HTTP上面的资源。

说到这个东西总是扯远,但是如果理解到了这个层面,我们知道原来这些技术技术是一个分层的结构,它要解决一个巨大的问题,但是却需要走一个漫长的路程,做到每一层都有所受益。从Restful本身来说它可以是一个计算机消费的资源访问API(里面可以封装Business logic),交换数据应该使用一个结构化的格式,HTML是一种天然的选择。如果作为阅读,人类需要美观的外表和帮助交互的行为,这些是CSS和JS应该去解决的。在这些内容具备以后我们就可以用语义网的推演能力实现数据之间的语义交互,那么就真的是道法自然了。这样的一个逐级实现伟大目标的过程我们就可以称为“渐进式增强”,也就是Progressive Enhancement。上周末的Session我就是从这个角度出发神侃了1个小时的,期间插播了各种渐进式增强所需要注意的最佳实践,他们都是为了我们光荣而伟大的目标而服务的。如符合语义的HTML tag,与资源表现(消费与解析)密切相关的Micro formats,在micro formats理念驱动下的css selector设计,最小化的css selector权重设计和复用,Rails 3.0的行为声明DSL化改造(如使用JQuery时常用的$(‘符合micro formats元素的css selector’).hasSomeBehavior()的声明),传统Rails RJS的缺点,开始使用HTML的新增tagName作为class渐进等等。我不想在这里重复,那天的内容没有提前准备,所以有点天马行空……所有的这些看似风马牛不相及的最佳实践实际上都没串到了渐进式增强实现终极目标的这个过程。我们看到现在桌面应用中微软的WPF也从Web的结构中吸取了经验,提供了一个结构、行为、表现分离的模型,而Flash所用的MXML+ECMAScript+CSS也是这个路子。

因为这个博文毕竟是个牢骚帖,所以我不想在这里阐述什么是渐进式增强,也许以我的能力根本说不清。我计划着用更多的时间多分几次分别讨论相关的话题,这里只是个开始。

回到关于小马的那个讨论大家要不要那么温和的问题上,我希望我们还是放弃那些中庸,站到几年以后的Google的技术桟上面。我相信,如果Google推动Webkit成功的话,那些药不用Table实现一个透明边框的Facebook对话框的问题就根本不是个问题了。工程就是工程,我会妥协去用Table,我会做很多Work around,可是,可以折腾的时候我一定要折腾一下,推动那个理想的实现。

这个是在小马帖子上面的回复:

HTML要解决的是结构,所以我们压根不用考虑太多到底用哪一种标签来布局。因为标签本来就不是布局的。如果Web是一些数据的表现的状态转移,那么我们在HTML的各种标准需要找到的是数据的筋骨本身。在XHTML1.x的成熟和HTML5的初露端倪的时候,我们已经可以开始幻想YAML、JSON、HTML作为数据源的标准表示方式了,那些表现我们还是继续留给那看似不完美却已经足够强大的CSS吧。偶尔,我们做些妥协,所以我们决定的不妥协正是对基于开放标准成为主流所贡献的一份力量。

那么,我们不要讨论HTML5什么时候会实现了吧,我们现在就开始为基于开放标准的语义化Web做出一份贡献吧!

听陆亦斌的iPhone开发故事笔记

今天中午我很崇拜的robinlu同学到我们办公室做了一个很精彩的讲座,题目是《我与iPhone开发的故事》。内容很精彩,我们都高度的享受了一把。顺便做了个简单的笔记,不敢私藏,分享出来。

陆亦斌@robinlu

目前在 IN-SRC Studio

07年开始做caibangzi.com

08年caibangzi.com工作变得比较少

从08年底三个合伙人开始做一些外包的工作(组织了IN-SRC Studio)

iPhone开发是robin的业余项目,20%

04年开始用Mac(powerbook)

时间线:

Jan, 2007 iPhone announced

Jun, 2007 iPhone released

Sept, 2007 iPod touch released

Mar, 2008 IPhone SDK (解决了只可以开发Web和基于toolchain应用的问题)

May, 2008 robin身边开始有一些人开发iPhone应用

July, 2008 – App Store Launched, Robin lu开始学习cocoa

Aug, 2008 – Release iCHM 1.0 (耗时2周),(绿皮的书,Cocoa),中国网站上开始告知如何申请IDP(很快又不见了)

Sept, 2008 Robin lu aplied for iDP

Oct, 2008 A lot of things happened, iPod touch 2 released

Nov, 2008 iCHM for iPhone released (第一天下载4个,第二天19个,第三天15个……每日7-10个)

Dec, 2008 Lite & BuddyFeed(上了Lite以后,iChm每天只有3-5个下载。将收费版本上升为6.99$,销量没有变)

Jan, 2009 – BuddyFeed (1月16日上,有很多反馈,19日第二版本,22上线,一开始有很多人关注,后来销量又变为个位)

Feb, 2009 – BuddyFeed 1.2 (销量平平)

March, 2009 – A talk with Ericsson

April, 2009 – Work on BuddyFeed 2.0

May, 2009 – BuddyFeed 2.0 released

Tweetie的作者是Apple的一位内部人员,而Apple对API的开放程度不好。UI设计的非常棒。Robin从里面学到了很多,而后开发了FriendFeed的2.0。产品曾经到过每日上百的下载量。受到了很多正面的评价。

Lessions

  • UI Design 至关重要(复合Guideline,也要让应用更好看):Weightbot, Tweetie, Facebook(open source了), Classics(电子书阅读器)
  • SEO:一开始叫iConnect,每日只能有几个下载,后来叫Twitter3D,每日能有50个。字母顺序靠前的应用在字母表排序的时候占便宜。
  • Lite:通过试用产品推动收费产品的销量,但是要根据不同类型的产品而定(对游戏非常有意义),对于 robin同学的应用来说没有快速的效果,但是对于所有应用从长期角度上来说总是有好的效果
  • $0.99:最低定价的价格,恶性竞争?如果已经是Top 10的产品,通过降价,可能冲到Top 1实现更好的销量。但是对于小众的软件来说,通过降价不会销量的上升,所以高一些的定价会提高收益。
  • Getting Real:尽早让产品被发布出来,被用起来,收到反馈并改进。iPhone开发的一个麻烦就是审核周期过长(一般平均要2周左右),这个对频繁的发布改进循环是个障碍。所以在产品最初发布的时候就一定要尽量提高质量,将功能做的比较全,这样才能吸引关注并且得到收益。
  • Idea:点子很重要,大部分销量高的软件都有很好的点子。质量一般相对居其次。
  • Expectation:如果准备要做iPhone开发一定要有一个很低的期望(降低期望),有那种买彩票的心态。做的更长一些,更有机会。

收益:从08年11月到09年4月,有3个Pro的价格。

下班的墨菲定律

下班的时候墨菲定律经常会发生,哭笑不得。今天又是一回。

我们一般18:00或者18:30下班(看Team安排)。今天北京下雨,老婆说下班在立水桥接我。昨天老婆说要我18:00出发,结果我实际18:27才出来,被数落一顿。今天长记性了。下午四点老婆打电话叮嘱今天一定18:00出发,我斩钉截铁的答应了。

到了17:00,我的Pair钱钱同学有一个会要参加,先离开了。我们今天做的Feature还有一些,我就继续往下做。到了17:30的时候我继续开发的部分不是很顺利,这时发现是我和钱钱之前重构的一个方法工作不正常,已经Break了不少单元测试,一时汗下。我看了一下,没什么头绪。这个时候当下已经攒下了1个多小时的修改没有Check in了,我review了一下修改,发现最好不要revert重来,可是20分钟我似乎也解决不了这个问题,而我一定要在20分钟后写好work update走人。正在踌躇的时候……

WPC对我说,你好像Break了Javascript单元测试……而且,那个Cruise agent被hang住了,需要重新启动一下那台服务器。顿时更汗了。想起上次Check in的时候只运行了precommit(就是提交到持续集成服务器之前的本地快速测试,只执行单元和快速的functional测试),没有验证javascript测试。嗯,认栽,前天写这个单元测试的时候我和Linc同学就怀疑过它的脆弱性(因为这两天在尝试Acceptance Test驱动,从上到下有些摸不到头脑)。

所以现在必须Git stash起来,切回master去修build。还好git在这方面表现很迅速。

切回来一看,Test suite里面的三个测试都break了。在有firebug的ff3里面运行jsunit还是没有线索……没有抛出exception,所以没法快速定位到代码里面去。只得先限定执行单个测试,但是依然没有找到原因。这个时候是18:57分,我心想:这下要死了,倒霉。要求自己冷静一下,一行一行的在firebug里面运行代码,发现这里的几个对象的构造参数已经被我们做了很大的重构,赶紧修改过来。然后继续跟踪,找到了出错的地方。原来是一个html元素的id从’_’分割改为了’-‘分割,修改过来。这个测试通过,解除另外两个的注释,也通过了。立马check in,然后git svn dcommit提交上去。此时已经18:03。赶快登陆cruise查看hang掉的agent的机器名,再登陆mingle09,在wiki上面搜索build vm的信息,找到这台主机的登陆方式。ssh上去并且和WPC确认reboot既可,马上sudo reboot。此时是18:05,走快点也许能赶回这五分钟。1分钟内把Macbook pro和电源打包,ipod touch和各种零碎放到包里面,飞速和大家say good bye逃跑出来……此时是18:07……

点天过了15秒才到11层,然后墨菲定律再次发生,10、9、8、7、6层都有人上电梯,这个要blame倒霉的诺基亚西门子,他们的人总是再这几层稀稀拉拉的拉拉稀稀……不情愿的在1层夺门而出,麻利的奔向13号线……一溜烟,而后墨菲没有来……

真的不知道为什么这样,倒霉在这几个点:

  1. 需要你的pair的时候他会不在,他在的时候……
  2. 着急做完所剩无几的功能,却发现前面的重构break了另外的功能(这个赖自己)
  3. 欲速则不达,越想马上完事,却发现还有更多的事要搞定
  4. 本来遇到麻烦发愁如何解决,却又被加上了一个严格的时间限制
  5. 马上就要到deadline了,确发现有更重要的事情要做(Break了持续构建)
  6. 更惨的是,你Break的测试hang掉了,需要重启服务器
  7. 而你还必须冷静的fix那些测试
  8. 那些测试没有啥明显的线索去fix
  9. fix之前你还要处理原先做的那些功能的代码
  10. 即使fix了也提交了,你还不能拍屁股走人,重启服务器你就要知道怎样登陆
  11. 登陆信息你要登陆Cruise和Mingle……谁让你平时没登陆过呢(如果经常维护Build也许你就对这样的串门很熟悉)
  12. 登上去以后你还要问人家怎么解决这个hang掉的build(同上,谁让你很少去维护基础设施呢)
  13. 即使你可以走了,电梯里面那些悠闲的从楼上乘电梯下一层的人让你冒火

无论如何,承认墨菲定律就是会在你着急下班的时候生效,然后找个地方凉快凉快吧。

我的pac文件

Update:这个文件有bug,看这个生成脚本

中午吃饭的时候另外一只手可以用键盘爽快的使用Google Reader,谢谢这个好工具。可是李笑来的博客不能访问了,很不爽。所以只得打开pac文件加上这个网站,随后一想,应该共享我的pac文件,大家也可以相互分享出来。我是根据Livid同学的这篇博客改巴改巴出来的。

function FindProxyForURL(url, host) {
  //Change it to your proxy
  var socks_proxy = "SOCKS 127.0.0.1:7777";
  
  //Add your favorite gfwed sites into this list
  var gfwed_sites = [
    "wikipedia.org",
    "apple.com",
    "tumblr.com",
    "soup.io",
    "pandora.com",
    "last.fm",
    "adobe.com",
    "photoshop.com",
    "google.com",
    "googlecode.com",
    "blogspot.com",
    "appspot.com",
    "acer.com",
    "demonoid.com",
    "alexa.com",
    "wikimedia.org",
    "flickr.com",
    "yahoo.com",
    "zend.com",
    "aptana.com",
    "uncyclopedia.tw",
    "uncyc.org",
    "youtube.com",
    "ytimg.com",
    "webarchive.org",
    "pornhub.com",
    "youporn.com",
    "twitter.com",
    "facebook.com",
    "wikipedia.org",
    "mail-archive.com",
    "versionsapp.com",
    "xiaolai.net"
  ];

  for(var index = 0; index < gfwed_sites.length; index++) {
    if(shExpMatch(url, "*." + gfwed_sites[index] + "/*")){
      return socks_proxy;
    }
    if(shExpMatch(url, gfwed_sites[index] + "/*")){
      return socks_proxy;
    }
  }

  return "DIRECT";
}


应该也可以从这里下载:tin.pac

参考:

  1. Livid's Paranoid - PAC & SSH
  2. Auto-configuring Proxy Settings with a PAC File

MobileMe试用之旅-产品对比评价

和GTD这样的好东西一样,理念最重要,方法其次,工具和载体最次。MobileMe整合了非常好的服务,他们都是很好的习惯,学会试用这些服务并用好他们相当于学会一种思想和对应的一种方法(如Address Book和Calendar还有Note,帮助你实现GTD),是否选择MobileMe非常不重要。看看这个产品分析,马上给你也整上一套这样的服务是最好的收获。热门的Google Wave是一个协同(Collaboration)工具,Google的基础服务实际上已经提供了协同的平台(代替Lotus Notes和Exchange,其实google enterprise就是这样的产品),那么MobileMe其实就是一个个人的协同软件,只是它还没有提供象Wave那样厉害的协同算法,但是Apple实际上是希望通过MobileMe能够给未来留下一个协同平台的种子,这个是我能想到这个很酷的Me.com的域名所能映射到的Apple的伟大梦想。

从功能角度上面来说Mobile Me的几个核心功能:

1、邮件:前面分析了,从空间上Me是10G,这个比目前Gmail的8G要大,不过这个基本上就是聊胜于无的东西。从用户体验上来说Me和Mac原生的Mail一样,优雅,简洁,不过从web上使用的感觉来说Gmail提供的感觉更好,它提供了换肤的功能,还有那些熟悉的标签,会话,归档等等,因为我个人非常喜欢Gmail,所以转移到Me上面基本上是不可能的。不过我个人认为me.com的域名很棒,所以在这个上面有个邮箱梃拉风的,而且懂行的就是知道你是使用相对昂贵的mobileMe的有钱人。MobileMe最NB的特性还是Web和桌面的超级一致,不过一个原因是桌面的mail本身界面和功能都比较简单,所以在Web上面实现一个这样的服务会简单一些。邮件是一个粘性巨大的服务,个人认为从运维能力上面说Google比Apple有优势,所以长久考虑使用Gmail会比使用me作为主邮箱要靠谱。Gmail基本上是Google account的一个核心,所以享用Google的SNS能力还有各种NB服务来说,gmail.com是一个有很高附加价值的免费服务的核心。这点上我基本上找不到太多me.com的吸引力了。

2、Address Book:这个功能我觉得是Mac平台有非常有吸引人的功能。西方的同事一般都非常重视联系人,而我本人以前一直不算注意。Gmail里面的联系人我一直没有很好的管理。现在回想起来很多联系人信息由于没有维护现在已经想不起来谁是谁了。Mobile Me同步信息在Address Book上体现了它的优势。但是,由于现在Address Book已经支持了与Google和Yahoo的联系人服务直接同步,所以MobileMe在这里又体现不出来优势了。但是MobileMe再次提醒了你,要定期合并重复联系人信息,给联系人分组,删除过期信息,把商业名片同步过来,这些操作是很好的习惯。如果你付费买了MobileMe,面对如此简洁的人机交互,我想也许它会督促你去使用它,并且逐渐养成这个习惯。

3、Calendar:Google退出Calendar服务的时候很多人惊叹它居然和Mac上面的Calendar的原生界面如此的相似,拖拽的去安排日程是个不错的体验。但是忙碌的朋友知道如果没有一个秘书帮你指定他们的话,一般个人也就是会使用天或者周这样粗糙的单位来管理日程,这样,那些漂亮的用鼠标划时间段的交互实际上你用到的没有那么多。这样说,一个界面不够华丽的Calendar从功能上并不会让你烦恼。现在的很多GTD工具都可以和Calendar服务结合,这个实际上是对于大部分朋友最有意义的。但是,Google的Calendar服务也早就可以和Mac的Calendar同步了。Thunder bird这个跨平台软件也很有竞争力,且它也可以和Google同步。

4、Photos:iPhoto 09是我最喜欢的一个Mac软件(另外一个是iTunes),Face和地理信息是非常棒的功能。当然iPhoto 09比起以前版本最重要的更新就是对社会化网络的支持:Flickr是最棒的社会化图片分享平台(另外两个我觉得不错的是Smugmug和Zenfolio,他们有不同的风格,其中Flickr是社会化做的最好的),另外还支持Facebook和MobileMe,好像不直接支持PicasaWeb(否则估计很多人就不会付费买Flickr的Pro服务了)。其实这已经摆明了我的意见,我在这3个很棒的服务里面选择了Flickr,看重了它的图片社会化分享能力,Flickr的Pro对外链支持不错,可以作为blog的图片服务,$24.9的价格基本上可以接受(听说国内打上收费旗帜的有播已经不行了)。扯远了,因为我对Flickr的绑定,所以我基本上不会选择MobileMe的Photos服务了。从简洁上MobileMe的Photos不会超过PicasaWeb(它还在Web实现了同样的人脸识别,而且直接可以标识出来和Google Accounts整合,无比强大),从复杂的功能(我喜欢它的License管理)还有社会化分享来说它比不过Flickr,从专业和商业友好上来说它比不过ZenFolio(识一个专业摄影师的业余图片交易平台)。所以,虽然这个是一个很棒的服务,可是无奈比它棒的服务也很多,那么它基本上就失去了对我的绑定,我可以继续享用iPhoto 09这个客户端,却选择同步图片到更好的服务上去。最后,还是要说,MobileMe Photo的界面很优雅(颜色和布局),4种View很独特,和Mac上面的体验比较一致,从程序实现的角度,我很喜欢它。

5、iDisk:非常棒,和操作系统集成的很好,支持外链很棒。而且iDisk的磁盘空间可以和Mail公用(这个没有什么意义,Mail用太多了不靠普)。最低的方案是99$,包括邮件总共20G,默认邮件10G iDisk 10G。可惜现在的文件分享服务已经很多了,免费的Dropbox也是User Space File System,无缝集成,也可以从Web上面访问(不过分享的时候比较容易超过限额,可是一边和朋友分享足够)。那么iDisk的吸引力就差多了。DropBox的跨平台能力更强(支持Linux)。其它文件分享如Drop.io、RapidShare作为临时文件分享也很好。但是这些服务对于拥有VPS的同学可能也不新鲜了。

6、Back to My Mac:可能有人会忘掉这个功能。但是对于程序员来说这个可能才是最有吸引力的能力!它可以保存你的各种Certifacate。并且在你的几台Mac都可以访问到互联网的时候它可以让你安全的让这几台电脑相互访问和控制,听起来很像VPN服务。对于远程控制家里面的下载,或者让Mac控制家中的电器,还有在回家之前在家里准备好迎接主人的音乐等等都是非常有意思的场景。让人兴奋。不过问题还是有,那就是VPN自己也可以搞,只是假设VPN需要你有带公网IP的主机作为VPN server,而且配置还是比较复杂的。我在我的linode上面配了一个可以本地链接的VPN就花了我一整个晚上(Mac上面有Tunnelblick做客户端,我用了另外一个晚上也没有搞定自动push路由,遗憾的很)。我觉得省心的使用Back to my Mac这样的功能(这个没有跨网VPN的能力,也就是你不能通过这个VPN跳板到国外上网,除非你把Mac本本放在外国)还是挺有意思的。

7、其它同步服务:Safari书签同步、keychain同步,Dashboard同步,这些小功能价值很高,一站搞定了。如果只是怕丢,可以用TimeMachine或者SuperDuper。如果省事,那么可以考虑使用那99$一年的服务。

最后,阐述我目前使用的方案。我不会选择MobileMe,因为上面试用下来没有找到任何捆住我的服务,而且甚至连一个让我觉得非常想去付钱的功能都没有。我现在试用了免费的Goole Calender、Google Contacts、Picasa Web、DropBox,还有收费的Flickr、VPS使用Linode,这样下来我得到的功能比MobileMe多,不过也更贵(VPS每月20$)。但是,很多朋友知道,淘宝上有99人民币一年的MobileMe服务可以买,这样倒是让它一下子有了巨大的吸引力,可是和那些便宜的iTunes卡有同样的问题,一旦你习惯了它,下一年却又买不到这么便宜的服务的时候,你会不会心疼呢?我支持付钱享受服务,但是,我希望那是比MobileMe目前提供给我的体验更好,功能更强,更稳定的服务,似乎Google目前最有能力给我们提供这样的服务。