2010年3月13日星期六

40岁的程序员

今天在csdn上看到一篇帖《程序员四十很尴尬》,其中的文字让我也觉得很尴尬:

……
领导说有人要来公司,让我去见见,沿海大企业上过班,老大意思是,年龄偏大,技术过时.但朋友推荐,又不好拒绝,言下之意让我婉拒.

等我赶到,人已经到了,中年人,40岁左右,果然有些偏大,领导说了一下公司近况,很长很含糊.我基本上不知所云,努力观察来人.说实在的,人到中年,做 编码显然已经不合适,应该可以看得出来是在行政部门待过的人,果然他自我介绍的时候提到以前在政府部门上班,然后下海,去了沿海,在一家大公司上班.据说 规模过万人.做erp,会vb和FOXPRO数据库,.net也会,但是不熟.他很努力的试图表达自己在用户体验方面有些经验,他的到来可能会在这个方向 上给公司产品带来提升,在外面久了,想回来.今天特意过来了解一下.也明确的说:自己现在的位置很尴尬.开发已经做不了,想在公司寻求维护的相关的职位.
……

尴尬之处显而易见:
  1. “人到中年,做 编码显然已经不合适”:自我心理暗示,这既是楼主自己的想法,也是来面试的老大哥的想法
  2. “以前在政府部门上班,然后下海”:半路出家,没有技术背景,起点不高。当然,这样的情况大有人在
  3. 在大公司做erp,使用vb/Foxpro,.net会而不熟:.net时代之前的vb?Foxpro?这都是上个世纪的东西了……

这就是我们上一辈程序员的结局么……他们之中有成功者,但这位老大哥显然不是他们之中的一员。CS/IT这个行业还非常非常的年轻,分支繁多,变化极其迅速,人的能力只能学习和追踪一个小的领域的技术和动态。即便是Python这么小众的语言,信息量仍然大的我无法接受。但如果不在每天工作之余看书看文档,落后是必然的。这位四十岁的程序员只是一个极端的例子,其实看看自己身边的许多三十多岁的同事,你就能看到自己的未来。有的同事虽然已经成家,但仍然在锐意进取,学习和尝试新的技术和框架,跟踪业界最新的发展;而有的同事已经被家事、被孩子、被各种琐事拖累得没有力气再前进了,只能吃老本了。有人说程序员做不过三十五,但我觉得那些不断前进的同事不显老,还能和我们这些刚毕业的小年轻争论问题,他们的经验和见解让我获益良多;而和有些同事在讨论问题的时候我们的想法会出乎他们的意料,我们的做法会让他们充满疑虑,原因很简单,他们没有学习了,不了解这些新东西了。真的是应验了文章里的那句话:

我这个位置很尴尬

这又让我想起了Douglas Crockford(JSON数据格式的发明人)。这个糟老头干过程序员也干过CEO,做过技术也做过行政。但唯一不变的是,人家没有丢掉那颗活到老学到老的心。我看过他的许多演讲,平易近人幽默风趣,像一位老教授一般。想想发明C语言的Ken Thompson,他老人家现在又被google请出来写Go语言,仍然宝刀不老;再想想自己接触过的Richard Stallman,他仍然在用命令行,仍然在写程序,仍然有活跃的思维。

十年太短,我不想在十年后就放弃写程序

2010年2月28日星期日

CherryPy的一个bug

web2py开发模式的web服务器是直接copy的CherryPy的代码,前段时间我们意外的发现了这段代码的一个小问题,后来查了一下,发现CherryPy也仍然存在这个问题。

[http://www.cherrypy.org/browser/trunk/cherrypy/wsgiserver/__init__.py?rev=2650#L566]
566        # Unquote the path+params (e.g. "/this%20path" -> "/this path").
567        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
568        #
569        # But note that "...a URI must be separated into its components
570        # before the escaped characters within those components can be
571        # safely decoded." http://www.ietf.org/rfc/rfc2396.txt, sec 2.4.2
572        # Therefore, "/this%2Fpath" becomes "/this%2Fpath", not "/this/path".
573        try:
574            atoms = [unquote(x) for x in quoted_slash.split(path)]
575        except ValueError, ex:
576            self.simple_response("400 Bad Request", ex.args[0])
577            return
578        path = "%2F".join(atoms)
579        self.path = path

574行中,url路径被分割后全部逆转义(unquote);在578行中,url路径被用"%2F"(即斜杠“/”)恢复。

问题就出在578行。"%2F"是一个转义了的(quote)字符,url路径也应该是被转义了的,而显然atoms列表里的所有元素都已经被逆转义而没有被再次转义。将url逆转义应该是应用程序的责任。这个bug会让应用程序在开发期间产生错觉,因为从开发服务器得到的url都是被转义过的;而在生产模式使用fcgi/mod_python部署之后应用程序得到的url就变成了没有被转义的原始url,措手不及。

2010年2月3日星期三

说说 web2py

很多人使用python进行web开发的第一个框架可能是django或者web.py,但我接触的第一个框架是web2py。在web2py上进行开发也有一年的时间了,我们还是决定切换回django。web2py有很多让我们不爽的地方,在这里进行一下不完全总结:

1、死板的url
一般情况下,web2py的url是有固定模式的,即/app_name/controller_name/function_name/arg/arg?key=val&key=val。将app、控制器和函数的名称和url直接联系起来,这是web2py的特色,但这样做最明显的问题就是url会变得很长,即使是如首页这样明显应该使用最短url的地方也是如此。web2py提供了一个routes.py来解决这个问题。在routes.py中,开发者可以像许多其他框架一样使用正则来重定向url。但这不是开倒车么?早知如此,何必当初呢。

routes.py还会带来另一个问题,因为routes.py所在的位置是web2py的目录,而非每个app的目录,也就是说routes.py是独立于app的。在进行版本控制的时候,如果开发者只对自己app进行版本控制,这个routes.py就会游离于版本控制之外了。最好的办法是将整个web2py都纳入版本控制,因为在开发的过程中你会知道,你需要自己为web2py写些补丁才能正常的工作下去。

2、url只支持某些字符
在有web2py特色的url中,如果你使用了中文的参数(args),web2py会给你两个字:Invalid Request。为啥?因为web2py对args做了严格的限制,只允许大小写字母以及几个有限的符号。对于其他符号,由于正则表达式不匹配,web2py会找不到控制层函数,于是就丢给你这么一个出错信息。url中的中文一般都是经过了转义的,即使你不去转义,浏览器也会帮你转义。比如“你好”,到了url之中就会变成“%E4%BD%A0%E5%A5%BD”,于是web2py就犯傻了。

我们修改了gluon/main.py中相关的正则表达式解决了这个问题。

3、过于独立的app
web2py的app是非常独立的,有自己的静态文件和session,不同的app之间没有预设的共享机制。这样的app划分方法可能只适合于承载多个互相独立的小站点,而对于一个含有多个相对独立的复杂模块,而模块之间又需要互相通信的项目来说,web2py的这种方式就完全不合适了。在这个问题上,web2py和django形成了鲜明的对比。

4、巨慢的启动速度
启动web2py要多久?大概5-10秒之间吧。web2py在启动的时候会import大量的库以及完成检查定时任务等工作,重启web2py是很耗时的。但是python的web开发有这么一个毛病,那就是有时开发者在修改代码之后不得不重启服务器才能看得到改动的效果,这在我所见到的所有框架中都或多或少的存在。重启django的开发服务器需要多久?web.py的呢?再回头看web2py,真是慢得跟蜗牛一样了。

5、出错页面极其简陋
在开发过程中,出错是少不了的。web2py的出错页面就是一行小字:ticket xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,然后你就跟着这个叉叉页面进行认证之后才能看得到出错内容。其实使调试页面可以通过认证访问是一个很不错的主意,我只是很不明白为什么要在开发模式下也这么干,更不用说web2py根本没有区分生产模式和开发模式的方法了。web2py的调试页面本身也相当简陋,就是把traceback打印出来,再把出错的源文件整个dump出来。而其他框架是怎样的呢?当django出错的时候,web.py出错的时候,trac出错的时候,moinmoin出错的时候,出现的页面是什么?那上面不但有traceback,还有traceback每行代码的上下文,有出错现场的所有局部和全局变量的值,还有当时的http请求和回答的内容等等。这种调试页面几乎已经成为了标准,我不明白为什么web2py视而不见。

好在将标准的调试页面迁移到web2py并不是一件很困难的事情。我们自己写了一个补丁,将这个页面从web.py中迁移了过来。

6、和python的标准模块有过节
在开发过程中我们发现我们的日志出了点小问题,日志条目包含大量的重复,而且日志似乎不是实时的,logging模块的日志切分也不正常。一开始我们以为是自己的代码问题,查来查去也没查出个所以然,于是就到web2py的邮件列表里搜索,发现有人报告了同样的问题(https://groups.google.com/group/web2py/browse_thread/thread/e20d0bd2e542aa14/a660bac70d5611a4)。比较奇葩的是创始人mdipierro竟然也不知道这个bug的根源在哪里,而且也没有任何解决的动向。帖子的楼主找到一个绕过问题的方法后大家都欢呼雀跃,然后就没人理这个问题了……

顺便说一句,web2py项目从07就开放了,09年才有人暴出logging模块的问题,难道用web2py的人都不写日志的么?

web2py还会和哪些标准模块过不去?谁知道……

7、magic(过度设计)
把事情变得傻瓜化,在用户不知情的情况下就帮他们做这做那,这是windows的哲学,我很不喜欢。很可惜,web2py就是这样的一个框架。web框架的确是应该帮开发者干一些脏活累活,但是也要弄清楚界限,否则吃力不讨好。我们在web2py中就两次遇到了这样的情况。

第一次是在构造一个文件下载的服务时,发现ie6/7下载总会出错,但是代码上没有任何问题。在StackOverflow上询问过后发现是web2py很热心的帮你设置了很多header。讨论帖请见:http://stackoverflow.com/questions/1999950/download-link-fails-in-ie

第二次是在处理url时,发现args里面的空格不是被转义成%20,而是都“自动”变成了下划线。察看web2py的源代码,发现果然是web2py搞的鬼。web2py的文档提过这事儿么?一个字都没有。

8、测试?None
我真的很羡慕django的开发者,django为他们准备好了一个非常棒的测试环境,并且在文档中给出了很多例子,帮助开发者完成代码级别的测试。而web2py呢?None!没有文档,没有例子,甚至连web2py本身,也有很多没有被测试覆盖到的地方。每个app倒是有个test目录,但怎么用?有没有best practice?web2py最官方的manual上都没说这件事儿。

9、DAL就是个笑话
现在python最好的ORM非SQLAlchemy末数了。看得出来,DAL是以SQLAlchemy为目标的,但是很可惜,画虎不成反类猫。DAL生成的数据表的migrate选项是一个很好的点子,但可惜做的不是很成功。它重度依赖自动生成的.table文件,在切换数据库时经常造成DAL无法识别明明已经存在的数据表。要说bug,DAL最严重的问题大概是会把布尔类型的字段创建为char(1),然后用'T'和'F'来表示“真”和“假”。匪夷所思是么?的确,我一开始也被雷到了,但是雷着雷着也就习惯了。

10、MVC三层互通!
这是我们最为痛恨的一点了,web2py的MVC三层竟然是通的!什么意思?这就是说,你的代码在运行的时候,不需要import,model/control/view三层的所有变量以及web2py自身的函数和变量以及request/response/session这些全局变量都会被加载到一个命名空间之中去。你自己的代码干了什么,你还是可以控制的,可以让他们不要互相重名覆盖,但是你能保证你的变量名和函数名不会和web2py的出现冲突么?import机制是白发明的么?现在全世界的开发者都知道命名空间是多么的重要,竟然还有这种脑残的设计,实在让人无语。

光凭这一点,web2py就可以去下地狱了。



当然,web2py也不是一无是处,它还是有两个优点的:

1、独立部署
web2py的发布包解压缩之后就能运行,立即就可以进行开发而无需安装,这非常方便。web2py鼓励开发者将所使用的第三方库放在modules目录中,这样在部署的时候将web2py整体打包就可以了,无需依赖其他外部环境。当然,对于其他框架,使用yolk+virtualenv也可以做到这一点。

2、自带定时任务
web2py自带了对定时任务的支持,这些定时任务代码可以访问app的model层代码,也就是可以使用model层的代码操作数据库。这样定时任务就不需要再依赖操作系统,而且也可以使用开发环境中的ORM,很方便。



Anyway,web2py的作者是个大学老师,这个框架就是拿来上课用的。如果有谁还真以为这个框架能作出什么产品级的应用,那真是天真的可以。

2010年1月6日星期三

你最想掌握的技能是什么?

今天在StackOverflow上看到这个帖子:What is the one programming skill you have always wanted to master but haven’t had time?

粗粗看了一下回帖,有些人说到了一些我想掌握的东西:
* RegularExpression 正则表达式
* automated unit testing 自动化测试
* Object Oriented programming 面向对象程序设计

其他的虽然不是我的茶,但也蛮好玩的:
* Read The Art of Computer Programming, and say I understood everything without lying.(可能么……)
* 很多人都说想自己写个编译器出来,这真是和国内很多CS的大学生不谋而合。
* REALLY learning Emacs.
* Creating an OS like Windows Vista.......(LOL)
* C and hack into the Minix, Linux or BSD kernels.(这也是大多数CS学生的天真梦想)
* ……

我最想掌握什么呢?说真的这是个有点学生气的问题。工作了之后自身素质提高虽然是很重要的一个方面,但是怎么能够又快又好的完成工作才是最重要的。就我自己的兴趣而言,除了上面列出来的三点之外我还希望能够:
* Dive into PyPy
* Dive into Javascript JIT

2009年12月31日星期四

使用 jQuery 操作 SVG DOM

最近在尝试用svg构造一个小项目的前端,方式是inline svg,也就是把svg元素直接写进xhtml之中。像这样:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
      PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:svg="http://www.w3.org/2000/svg"
      xmlns:xlink="http://www.w3.org/1999/xlink">
  <head>
  </head>
  <body>
    <svg:svg version="1.1"
             font-family="Courier New"
             font-size="14px">
      <svg:rect />
    </svg:svg>
  </body>
</html>

注意这里是xhtml而不是html,如果你希望你的页面能够被Firefox直接渲染,这是必须的。xhtml是xml文档,只是非常像html而已。Firefox的xml解析器和html解析器是不同的,所以如果用html作为这个页面文件的扩展名会导致Firefox直接把源代码显示出来,就像显示一般xml文件一样。

svg dom和html dom没有什么区别,大多数方法也是相同的,所以jQuery在一般情况下也是畅通无阻的。只有一点是需要特别注意的,应该使用需要指定命名空间的DOM方法。比如如果需要创建一个svg元素时,应该使用createElementNS而不是createElement。好在这样的DOM方法不多,只有这么几个:

















































createAttribute createAttributeNS
createElement createElementNS
getAttributeNode getAttributeNodeNS
getAttribute getAttributeNS
getElementsByTagName getElementsByTagNameNS (also added to Element)
getNamedItem getNamedItemNS
hasAttribute hasAttributeNS
removeAttribute removeAttributeNS
removeNamedItem removeNamedItemNS
setAttribute setAttributeNS
setAttributeNode setAttributeNodeNS
setNamedItem setNamedItemNS
这个问题在svg wiki和SVG Authoring Guidelines都讲到了。

这就带来一个问题,jQuery是不使用这些结尾是NS的DOM函数的。怎么办呢?笨办法是直接git clone出jQuery的源代码,想把所有相应的函数全部用NS结尾的函数替换并改写,然后重新编译出一个定制版的jQuery给自己用。我怎么知道的呢?因为这种蠢事就是我干的。还好在用脚本替换了这些函数之后我还是意识到了,转而把这些NS结尾的函数包装成jQuery的插件,比如像这样:

//包装hasAttributeNS
jQuery.fn.hasattrns = function(key) {
    var ele = this[0];
    return ele.hasAttributeNS(null, key);
}

这样包装完这些函数之后我就可以继续使用jQuery了,这些新函数和jQuery配合的还真不错:)

顺便说两句svg和canvas。从web的角度来看它俩都能在浏览器中创造图形元素,提供更好的用户体验。但从另一个角度来开,<canvas>是html5的一部分,是专门用于增强web页面的;而svg属于xml,它不局限于网页,还可以比如和xul结合起来增强Firefox应用程序本身的使用体验。正像我上面所说的,xml和html是两回事情。不过现在xhtml2的工作组被解散了,xhtml前途未卜,svg在网页之中的表现会如何呢?至少在目前我感觉canvas的应用日见增多,但svg却还是在原地踏步。

2009年12月28日星期一

我的 GoogleReader 订阅

今天和jessinio同学聊天,扯到RSS订阅的事儿,在这里分享一下自己的订阅:google-reader-subscriptions.xml

我喜欢技术,所以绝大部分订阅都是技术类的;我讨厌政治,所以我会拒绝政治类的分享。已经有同学因为分享太多和技术无关的内容而被我忽略了,比如双木成林。

我喜欢Firefox,喜欢Mozilla。我觉得所有这些RSS源中Mozilla Planet对我的帮助是最大的。当然,我也不排斥Google或者Microsoft,咱不搞意识形态挂帅,只要是好东西咱都愿意学习。

非技术类的订阅中我最推荐Matrix的数学Blog。只要你喜欢数学,还不想自己的脑筋生锈,就应该订阅它。

2009年11月20日星期五

Chrome OS

昨晚在公司熬夜看ChromeOS的新闻发布会直到四点,还好Google没有让我失望。ChromeOS和我想的基本一样,唯一的惊喜在于它的文件系统。ChromeOS的root是只读的,同时不允许安装任何二进制可执行程序。这是一个相当漂亮的设计,这样ChromeOS就只需要专注于浏览器的安全就可以保证系统的安全。

ChromeOS的发布意味着几件事情:
1、应用程序的在线化是大趋势,对于普通用户来说,桌面的消亡不可避免。但是我们的桌面上还有非常非常多的应用程序没有网络化,很多很多网站还没有将自己应用程序化。这是ChromeOS这样的操作系统普及的一道巨大障碍,同时也意味着一个巨大的蛋糕,一个巨大的机遇。
2、ChromeOS现在使用的都是google的服务,但谁愿意把自己的东西都交给google一家保管呢?也就是说,网络应用互相之间的接口标准化将不可避免,各种网站提供的标准接口的网络存储、邮件、歌曲等等服务会大大的丰富起来。

ChromeOS已经可以跑在上网本上了,PC还会远么?让我小小的憧憬一下未来的在线编译服务吧……

ps: 本来昨晚就应该写这么一篇,不过不出意料,那个时候google docs都挂掉了
ps: 很可惜http://chromium.org/国内访问总是被屏蔽,我要想办法checkout一份代码下来编译一遍才好。