一个时代的终结
明远正式宣布离开,他的时代终结了。
我在明远手下做事也有一年多了,回忆一下,我们从未有过正式的交谈,他大概也不会认识我。他是我老大的老大的老大,他是一个产品人,他喜欢足球,而我只是一个沉默的程序员,对任何体育运动都不感冒,我们没有聊头。不过我还是得感谢他,在我看来,是他让我在工作中找到了久违的激情。
明远很喜欢在季度沟通大会上不断激励我们的创业激情,讲述自己对未来、对竞争对手的分析,鼓励我们勇于接受挑战,实现心中理想。他的演讲很有特点,语调平缓,语速适中,整个过程很少带有废字,逻辑十分清楚,能用平淡的语言激起他人共鸣。他在演讲方面的老成与他的年龄很不相称,哪看得出他比我还小啊。
在团队活动中,我印象最深的是有次部门全体员工集体出游的时候他穿着短裤拖鞋混迹于员工之中的形象。明远没有架子,在团队活动中他就是个小孩,不修边幅,很不起眼。这个样子的明远才是最真实的,而他的内心也许根本没有那么成熟,站在讲台上的那个过分成熟的身影终究会成为限制他自身发展的禁锢。
由于种种我不知道的原因,明远选择了离开,有点突然。东宝为明远在公司这段惊心动魄的创业历程写了篇《为了即将忘却的纪念》,可惜我只经历了这段创业旅程的最后一段。(东宝的文章已经看不到,可以看其他媒体的转载)
明远,愿你在未来能永葆激情,创造更大辉煌。我看好你。
Firefox插件(plugins)开发实用指南
Firefox插件可实现强大功能,但其中麻烦事情不少。写这个实用指南首先是为了方便自己记忆,免得以后再次栽倒一些坑里面,如果能帮助其他人,则是更好。这个指南不是为了手把手教读者开发插件,而是作为一个FAQ,解决各种诡异问题。
Firefox拥有众多的扩展(Extension),开发扩展也非常容易,不过有一些事情还是无法用扩展解决,需要访问操作系统的底层功能,这就需要写插件(plugins)。例如flash就是一个插件而不是扩展。
Mozilla提供了一系列的教程和文档,虽然很不详尽,众多重要的API语焉不详,但至少是一个好的开始。
最需要阅读的是plugins API和使用入门。这是一个相当长的文档,如果看完所有的内容会花费大量的时间而且还会很晕,这里列一些重点供参考。
- plugins基础概念
- 写第一个插件(只需要关注Writing Plug-ins这一节所谈到的内容)
- 获得一份firefox的源码,比如firefox 3.6。plugins的例子可以在源码里找到(modules/plugin/sdk/samples),如果出了问题还可以自己编译一个debug版的firefox来调试。
- 了解浏览器能提供什么功能
- 制作插件的安装程序,推荐用扩展的方式安装插件,有无数的好处
完成以上这些内容以后差不多就已经可以实现自己的插件了,一般而言,参照着例子来做开发不会有什么问题,只是有不少细节需要留意。
Firefox plugins开发的众多奇怪的约定(假设plugins已经被正确安装)
有些约定非常奇怪,不要问我为什么,天晓得开发firefox的牛人们怎么想的。
在Windows下,plugins必须满足以下条件才能被firefox检测到:
- 插件的名字必须是np*.dll,也就是必须以np开头,.dll结尾
- 插件dll资源的语言必须为LANG_ENGLISH,code page必须为1252。在rc文件里是这么写的:
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
- 插件dll的VERSION_INFO里面必须包含以下值:
VALUE "MIMEType", "application/x-your-mimetype"
这个MIME就是<object>标签引用插件的唯一凭证。
在Linux下,plugins必须满足以下条件才能被检测到:
- 插件的名字必须是lib*plugin.so,即以lib开头,plugin.so结尾
- 插件必须实现NP_GetMIMEDescription和NP_GetPluginVersion,并返回合适MIME字符串。注意,这个字符串并不是普通的MIME,是有特殊规则的,详见前面这个链接的内容。
- 插件so不要静态链接gtk、opensll、pthread、z等系统库,这会在不同linux平台上因为符号表的问题遇到各种运行时错误
特别需要说明的是,NP_GetPluginVersion、NP_GetEntryPoints等关键函数没有任何官方文档介绍它们,只能根据例子来猜,反正没事就别改它们的实现,copy例子中的代码就好。
firefox插件开发注意事项
写firefox插件的一个基本习惯是,经常编译代码并运行它,保证你的插件还能工作。只要firefox无法加载dll/so,或者加载出现任何错误,都会悄无声息的忽略这个插件。时常关注一下about:plugins,看看插件是不是还在这个列表里。
firefox插件从窗口模式上可分为windowless和windowed两种。其中,windowless模式的文档较多较全,是firefox比较推荐的模式,坑比较少,这里就不多说了。windowed模式则相反,需要好好说说。
无论在Windows还是Linux上,windowed的插件都拥有独立于浏览器页面的窗口。firefox会通过插件的NPP_SetWindow来告诉插件当前窗口的情况。
关于windowed插件有两个诡异问题需要注意:
- Windows平台下,插件窗口默认会响应WM_CTLCOLOREDIT、WM_CTLCOLORLISTBOX、WM_CTLCOLORBTN、WM_CTLCOLORSTATIC消息,并设置一个默认的背景色。这本来没问题,但在Windows XP下,这个颜色居然永远是黑色,而不是默认系统背景色(通常是白色)。最好subclass这个窗口并且拦截这些消息,不要让firefox去处理它们。对于插件来说,firefox处理这些消息只是帮倒忙而已。至于firefox还帮了哪些倒忙,可以去源码widget/src/windows/nsWindows.cpp的nsWindow::ProcessMessage()去围观。
- Linux平台下,NPP_SetWindow传入的NPWindow指针中虽然有一个ws_info成员,这个成员里面也确实有一个display变量指向X Window的Display结构,但绝对不要真正使用它,否则可能会导致firefox直接退出,据说这可能是firefox的一个bug。
测试firefox插件小技巧,测试方面的高手可以无视
测试插件前建议先在firefox里面创建一个新的profile(帐号)。这样可以创造一个最干净的开发环境,避免被其他扩展/插件干扰。
默认的profile名叫default,在命令行里输入firefox -p default就可以使用这个profile。如果只是输入firefox -p,会弹出一个对话框用于选择profile。这个命令在Windows和Linux下都可使用。
无论是哪个平台,调试插件的方法都很类似。
Windows下可以用VC以调试方式启动firefox,载入插件时调试器会自动载入对应的符号,捕捉发生的异常或者设断点都很方便。
Linux下直接用gdb就好,细节应该不用多说。有一点需要注意,系统默认安装的firefox命令(默认放在/usr/bin/firefox)是一个shell脚本,真正的可执行文件名字需要打开这个脚本自行查找。
实现firefox插件的基本功能
firefox为插件提供的接口十分原始,很多功能默认没有实现,下面提供了一些思路和方法。
- 让插件接受焦点:默认情况下,<object>标签不能获得焦点,必须指定tabindex。
- 在插件中使用tab键跳到下一个element:没有好办法,必须自己手动将焦点还给浏览器窗口(Linux下不必如此),然后自己用NPN_*系列函数找到应该获得focus的DOM element,然后调用这个element的focus()方法。
- 隐藏和显示插件:直接设置<object>标签的style.display = “none”即可,但这里有个严重的副作用,firefox会调用插件的NS_PluginShutdown,销毁这个插件。如果不期望造成这种效果,要么别用这种方式隐藏插件,要么把插件状态保存在js里,再次显示的时候把状态设回去。
- 触发DOM事件:firefox没有提供任何便利的方法触发DOM事件,要在插件中做到这点,必须自己模拟js触发DOM事件的过程。例如,对于HTML事件,假设self是DOM element,js会这么做。
evt = document.createEvent("KeyboardEvent");
evt.initKeyEvent(
"blur", // in DOMString typeArg,
false, // in boolean canBubbleArg,
false); // in boolean cancelableArg,
self.dispatchEvent(evt);
对应的C代码就是
void FireHTMLEvent(NPP npp, const string & name)
{
NPVariant result;
NPObject *window;
NPVariant vDoc;
NPN_GetValue(npp, NPNVWindowNPObject, &window);
// 也许页面已经跳转了……
if (!window) {
return;
}
NPIdentifier sDocument = NPN_GetStringIdentifier("document");
NPN_GetProperty(npp, window, sDocument, &vDoc);
NPN_ReleaseObject(window);
// evt = document.createEvent("KeyboardEvent");
NPVariant evt;
NPObject* npDoc = NPVARIANT_TO_OBJECT(vDoc);
NPIdentifier createEvent = NPN_GetStringIdentifier("createEvent");
NPVariant eventArgs[1];
STRINGZ_TO_NPVARIANT("HTMLEvents", eventArgs[0]);
NPN_Invoke(npp, npDoc, createEvent, eventArgs, 1, &evt);
NPN_ReleaseObject(npDoc);
// evt.initKeyEvent(
// "blur", // in DOMString typeArg,
// false, // in boolean canBubbleArg,
// false); // in boolean cancelableArg,
NPObject * npEvt = NPVARIANT_TO_OBJECT(evt);
NPIdentifier initKeyEvent = NPN_GetStringIdentifier("initEvent");
NPVariant initArgs[3];
STRINGZ_TO_NPVARIANT(name.c_str(), initArgs[0]);
BOOLEAN_TO_NPVARIANT(false, initArgs[1]);
BOOLEAN_TO_NPVARIANT(false, initArgs[2]);
NPN_Invoke(npp, npEvt, initKeyEvent, initArgs, 3, &result);
NPN_ReleaseVariantValue(&result);
// this.dispatchEvent(evt);
NPObject * self;
NPN_GetValue(npp, NPNVPluginElementNPObject, &self);
NPIdentifier dispatchEvent = NPN_GetStringIdentifier("dispatchEvent");
NPVariant dispatchArgs[1];
dispatchArgs[0] = evt;
NPN_Invoke(npp, self, dispatchEvent, dispatchArgs, 1, &result);
NPN_ReleaseVariantValue(&result);
NPN_ReleaseObject(npEvt);
NPN_ReleaseObject(self);
}
暂时先写到这里,如果还有什么疑难杂症,欢迎评论,我继续添加。
我这程序员的一年
2008年12月30日,正是我在微软发farewell letter的日子。我当时已经拿到百度的offer,正在准备把自己的角色从微软的项目经理转换成百度的技术研发。角色的转变背后往往藏着各种故事,我当然也不例外。
从微软到百度,我只觉得这是我的幸运,并没什么值得夸耀的地方。我在微软的一年半时间里,技术上逐渐荒废,连自己也觉得堕落不已,有劲使不出。离开微软并非自我选择,但尝试走进百度,则是当时一个勇敢的决定,我重新抱起书本,打开已经陌生的Visual Studio,从头开始准备。
我参加的第一个百度面试是在普天大厦,部门是NS,当时有两个面试官同时面我。我用刻意的沉稳与简洁来掩盖自己的不安,整个过程好似梦魇,令我疲惫不堪。郁闷的是,也许因为HR之间没有沟通好,前一轮面试结束1小时后,我还要赶到信威通信,参加百度第二个面试,电子商务部的面试。本来在上一个面试中我已经把斗志与自信消磨的差不多了,我只好用自己的本色来面对这第二个面试。很奇怪,第二个面试反而比第一个轻松,我发现我的思维开始活跃起来,沉睡已久的程序员的细胞开始复苏,讲起各种技术竟也能变得流畅而不刻板了。
再之后,我觉得自己像撞了大运一般,两边的面试竟都走到了最后一轮,而且都还通过了,这个面试的经历为我今年的程序员之路开了个好头。
值得一提的是,在电子商务部最后一轮面试的时候,老大问我对未来的规划时,我犹豫了。我曾想,做项目经理、做管理似乎是一个程序员的必然发展道路,但对于我真的适合么?我已经厌烦那种push团队前进、营造团队氛围、制定远景方向这种事情,我更想这几年踏踏实实的做事,完善自己的知识体系。但当时我还无法打破自己的思维惯性,还是支支吾吾的说希望成为研发经理云云。直到加入百度半年后,我才坚定自己的想法,做一个简单可依赖的程序员,先从技术做起。
2009年1月,我加入了百度,这程序员的一年开始了,然后很快,这一年结束了。
我从没想过时间会过的这么快,这么紧张有趣。我从对Linux一窍不通,到现在都开始习惯完全用vi编码、在命令行中调试、负责服务器程序的优化,这种变化我自己都感到惊讶。
粗略统计了一下,这一年大约写了10K的代码,这个数字比一年前0行代码比起来当然是无穷大,但和原来本科七年写100K代码比起来,似乎也不算那么多。我当年做简历的时候就很惊讶,自己参与过的各种项目、自己写的各种小玩意,居然有那么多行代码,到今天我终于明白,其实这并不难,如果不是今年我把很多时间用在摸索上面,恐怕代码行数还得翻番。
我今年最大的收获是激活了程序员的基因,手指终于开始适应写代码啦,这是一个很好的开始。
本文写到这里似乎还没开始说这一年到底发生了什么,但确实已经要结束了。无论是接手康神留下的系统,还是从信威搬到百度大厦,这些都是外在的一些挑战与变化,相比自己重新选择未来道路这件事情来说,真的是微不足道。我这程序员的一年,恰好就是选择结果的体现,到现在我已经可以说,这个选择没有错,至少我比原来快乐。
架构成长之路
产生软件架构设计的动机是代码复用,当一段重复的代码在多个不同的模块之中重复多次、每个人都为这段代码写的吐血的时候,大家自然而然就想到了——我们要有一个“架构”,或者说准确的说是一个框架。
最原始框架一定是最基本的。从广义上来说,操作系统就是一个框架,它封装了每一个程序需要完成的基本任务,管理内存、磁盘IO、中断、输入输出等。不过操作系统能封装的东西有限,最关键的是,封装总是略显简单。比如,需要写一系列服务器程序,每个程序都要访问网络、保持长连接、管理连接池等,这些机制并不是几行代码就可以实现,需要一个稍微高一点层次的框架来解决这个问题。于是很自然的,层次的概念就出来了。
当框架有了层次概念之后,大家就可以各司其职,为完善各自的框架而奋斗,直到最终框架已经极为好用,每写一个程序就像冲杯奶茶一样容易。大家都很高兴。
到现在为止,软件架构似乎还非常的简单直白,但随着时间发展,复杂的需求涌现出来了,架构也就突破原来软件框架的范畴,变成更复杂的东西。
还是拿那一系列服务器程序为例,假设它们是一个在线交易平台的后台服务。某个程序员完成了个人转账业务的代码,另一个程序员完成了商户转账业务的代码,互相一比对发现,嘿,大家怎么写的大同小异,应该优化。不过这样的优化存在着风险,因为谁都不能完全确定,未来这两个业务会怎么发展。
保守的程序员会让这两份代码独立,静观其变,反正框架已经足够好,重写一份这种业务也花不了多少时间。这并没有错,不过会错失让架构突破框架限制的机会。
喜欢挑战的程序员会仔细思考这两个业务逻辑,从业务的层面来分析其中的异同以及未来变化的趋势。
比如转账这件事,无论是个人业务还是商户业务,创建交易、记账这些事情总少不了吧,这或许可以抽象出来,但是检查账户权限、验证密码这些事情肯定会不一样,这些东西保持尽可能的可变。如此这般,业务层面的封装逐渐成形,所谓的架构终于有了架构的意思。
在引入业务之前,前面提到的“架构”只是一个放之四海皆可用的程序员的玩具;引入业务之后,架构终于得以走向成熟,摆脱程序员的束缚,反过来影响程序员的思维。
真正让架构腾飞的是引入领域知识(domain knowledge)的概念。作为一个具有业务功能的系统,要维护业务的纯正性不容易。还是拿转账来说事,如果提供了创建交易和记账的业务抽象,再假设转账是一气呵成的,从代码上来说,先创建交易还是先记账其实没有太大区别。但这事搁在包含了领域知识的架构里面可不行,因为账务要求,必须先有单据再有流水。
领域知识把程序员变成业务专家,渐渐的程序员就更能明白什么是业务、什么算框架,也可以开始用业务接口包装子系统,理解子系统之间的逻辑,让架构日臻完善。
P.S. 上面这些文字并没有涉及具体的设计原则、方法。
P.S.S. 再完美的架构也需要持续改进,况且这世界上恐怕还不存在所谓“完美”的架构。
敏捷开发的几个误区
不少公司都在考虑采用敏捷开发,或者在项目开发过程中融入敏捷的思想,在这里,我列出几个常见的误区,希望能对大家有所帮助。
欢迎发表不同意见。
误区:敏捷开发 == 极限编程/Scrum/…
敏捷开发是一种方法论,只是一些基本原则的集合,并非具体流程。
极限编程、Scrum等流程是具体的实施方法,它们都声称符合敏捷开发的思想,但执行起来是否真的“敏捷”,还得看参与者究竟思想上是否真的接受敏捷开发的原理。
如果把结对编程、daily scrum当做是敏捷开发的表现,那更是本末倒置,可悲的是,不少人还真是这么认为的。
误区:敏捷开发 == 简化流程
敏捷开发不一定能简化工作流程,而且简化流程也并非提出敏捷开发的初衷。敏捷开发最重视的是拥抱变化,至于怎么拥抱,只能随机应变。实际应用中,既有流程相当简单的经典Scrum过程,也有极为冗繁、不亚于CMMI的RUP,根据应用场景不一样,项目组应该使用最适合的流程。
选择敏捷开发流程时也应带着敏捷开发的思维去选择,即快速响应项目实际的流程需求、拥抱流程应用过程中遇到的各种变化。没有银弹,也没有长期适合的项目流程,生搬硬套某个看似成熟的敏捷开发流程是大忌。
误区:敏捷开发 == 快速开始编码
敏捷开发强调迭代,鼓励开发人员用代码说话,不过绝对不鼓励没想明白就写代码。
符合敏捷开发思想的流程往往主张在一个稳定的基础之上迭代完成各种功能。如果基础都不牢固,迭代就无法进行,整个开发过程就退化成不断重写的过程,浪费开发时间。敏捷开发实际非常重视“设计”,并且对开发人员的设计水平提出了极高的要求,既要“持续重构”又不能“过度设计”,稍有不慎就会陷入反复推翻已有代码的窘境。对于内功不够的开发人员最好还是想好再写代码,设计的时候慢一点没关系,尽量少的做无用功才是最重要。
误区:传统开发能随时转变成敏捷开发
敏捷开发过于诱人,很容易让深受传统软件开发思想折磨的开发人员感觉敏捷开发就是灵丹妙药。
要想转变,首先需要从思想上正确认识敏捷开发的含义,了解它能解决什么问题、会带来什么新的问题、对现有软件/硬件资源有什么要求等。
例如在原先采用CMM的团队中,想利用敏捷开发简化冗余的文档、降低沟通成本,那么敏捷开发确实能在一定程度上缓解这些问题,但也会造成内部文档缺失、沟通不够深入的问题,在应用敏捷开发之前需要先确定适合团队的新的文档流程(代码注释能够自动/半自动的变成团队需要的文档么?),并且确定沟通的一些原则(比如,对于一些重要的沟通,是否还是用邮件来进行,不要过于“敏捷”?)。
有趣的是,有可能在回答这几个问题之后会发现,敏捷开发并不能解决项目中遇到的问题,反倒是其他方面出了问题。
举例来说。如果之前开发方法是简单的目标管理,遇到的问题是项目进度不可控、开发+测试的周期较长、不能及时响应需求变化,那么敏捷开发能解决的是快速响应需求变化,对项目进度和开发测试周期帮助不大(没有一种流程能够承诺改善项目的开发效率!),但前提是开发人员必须懂得怎样正确的去迭代开发,并且必须认识到一次迭代的结束是以完成测试为标准的。
在这个例子中可以看到,敏捷开发能带来的好处非常有局限性,如果开发人员达不到一定的层次是很难受益的。与其号召使用敏捷开发,不如想想如何增加执行力,以及找到周期偏长的根本原因(是因为设计不充分而返工?还是因为没有可以快速回归的测试用例?等等)。
淘宝视频和淘宝电子杂志:这葫芦里卖的什么药
淘宝最近推出了两个网站,淘宝视频和淘宝电子杂志,很有点不务正业的样子。


通过这两个网站的内部链接可以发现,它们都是淘宝与其他网站合作的结果,并不是淘宝自己提供的内容和技术。淘宝视频的内容来自激动网,淘宝电子杂志的内容来自ZCOM。淘宝只是提供一个整合的首页,在内容旁边放一些淘宝的牛皮癣广告,真正的流量则直接让给了上述两个网站。从现实好处来说,这两个本不著名的网站占了大便宜。
激动网和ZCOM为淘宝定制的网页都可以用淘宝账户登录并发表评论,这实际上意味着淘宝在变相的拓展产品线。对普通用户来说,打开淘宝视频或电子杂志首页之后,只要网页风格和登录方式统一,那么就相当于没有离开这个网站,他们不会管域名是否已经改变。未来如果淘宝真的想涉足这两个领域将变得非常自然,现在的做法只是在布局,在树立口碑。
毫无疑问,淘宝视频和电子杂志可以增加用户黏度,让用户不用离开淘宝就能找到所需要的东西,不知道这是否也是大淘宝战略中的一部分。之前淘浆糊出来的时候,就已经可以看出淘宝想利用SNS增加用户黏度的想法,现在再加上这些娱乐元素,将会继续朝这个方向发展。
从某种意义上来说,做这两个网站是淘宝的悲哀,也是中国互联网的悲哀。中国网站鲜有专注者,阿里巴巴集团一直都不专注,但如果单看淘宝网或许还算好,至少都在围绕着购物这件事情不断发展。但随着这两个网站的加入,纯粹为吸引用户的娱乐元素越来越多,淘宝越来越像门户,不知是否会变成第二个QQ.com。莫非是淘宝内部已经觉得自己在购物平台方面已经无事可做了?大概是他们的产品和研发都没空看淘宝社区吧。
究竟淘宝在这两个网站上能玩出什么花样还需要时间来证明,我等着。
Twitter Lists:Twitter新玩法
Twitter最近大规模的公测Lists功能非常有趣,跟其他平台中tag、好友分组等功能类似又不相同。
Twitter里面本没有好友的概念,所谓的follower和following也只是一种订阅关系。这个lists功能并不限于在自己的follower和following之中使用,可用于任何一个用户而不用建立关系。
Lists也可以被follow,follow之后就可以看到list里面所有用户的更新。
在这里我有一个有趣的想法,有了lists还要follower和following关系干什么?特别现在twitter用户数不断疯狂增长,对于一个twitter新人来说,该follow谁是一件非常头痛的事情。有了lists之后,新人只需要关注名人和名人创建的list就可以开始twitter生活了,这比原来好上很多。
想必twitter以后会推出热门lists,甚至于对lists进行评价和打分,增加lists的用户参与度,把lists变成一个新媒体的入口,赋予那些有话语权的著名twitter用户更多舆论导向的权利。从此以后,@Fenng大概也不用再来推友推荐计划了,只需要创建一个list然后宣传这个list就可以了。
从体验上来说,管理list里面的用户是非常困难的事情。
点击list的edit链接之后得到的是这个浮动层,不过并没有管理用户的选项。
如果只是删除用户那还好,只需要点击following链接,然后把用户从list中去掉。不过如果要新增用户就必须从find people或者其他用户的首页点击,在list界面上却完全找不到类似于add的链接。其实twitter只需要把find people包装一个接口放在list管理界面就很好了,只可以他们还没想到。
最后说说怎么推销自己的list。Twitter提供了一个Tweet about it的入口操作,同时还可以简单的使用@功能引用list,这个很赞。例如这个百付宝员工的list:@baifubao_news/staff
最后的最后,请不要问我怎么顺利的上twitter,我不知道,我只是机缘巧合的得到了twitter的信息并找到了上面的图而已。:P
体验为先
一般的想法似乎是:产品部负责体验,技术部负责实现细节,这两者虽相辅相成,但终究关注点不同。不过我觉得其实技术部也应该关注体验,这种体验是开发体验、运维体验和用户体验,至于实现细节,反倒是最后需要关注的。这样提的原因是,确实有一些技术的人一见到好的新奇的技术就像应用到项目中去(本身没什么不好),却没把体验这件事情做好(这个有点不好)。
现在来说说我对体验的理解。作为开发者,最大的目标绝不只是开发符合需求的产品。产品的开发、实施、上线、维护等过程都涉及大量不同的人,每一个相关的人对这个产品都有自己的体验,都会对这个产品在某一方面上是否好用有自己的看法。就像体验不好的产品会被用户弃用一样,过程体验不好的产品也会被开发者抛弃,只是迟早的事。
开发体验好也就是代码具有很好的观赏性、架构容易懂、代码质量高、具有可伸缩性等,它是开发过程中最需要注意的事情。
代码是开发者之间的交互工具,一个开发者写出来的代码其实就是其他人未来将用到的“界面”,它的体验直接影响到它的用户(所有与它相关的开发者,包括作者)对它的看法。开发体验不好的代码,在完成自己的当前使命之后很可能会很快被开发者厌倦,甚至废弃,如果一个组织不断在废弃旧代码,做重复造轮子的事情,这种开发成本当然很大,其中明显有问题。
这里有一个很有趣的问题,如果牛人写出来的精致无比的代码是否就一定是具有好的开发体验的代码?这还真不一定。如果确实周围的人一个都看不懂这代码,要么就不要把代码写那么好,浅显一点会更好,要么就多找一点能看懂这种代码的牛人。过于美妙而难以理解的代码是定时炸弹,一旦牛人离开就会面临难以维护或者必须重写的风险,这对于公司而言是不可接受的。说到底,体验是人的主观感受,不同的受众就有不同的体验,好的体验的定义也不同。
做好了开发体验,其次要做的是运维体验。运维体验主要包括更新服务、排错、监控的复杂程度等,主要的用户是运维人员。现在搭建一个大容量的网络应用至少需要几十台机器,机器之间的互相依赖如果管理不好就会变成网状,有点牵一发动全身的味道。有些时候开发者相比运维体验,更关心功能是否能实现,这是有问题的。对于互联网企业,不变的是变化,任何一个服务上线之后都会有用户不满意的地方,如果升级的运维成本比较大,开发者/产品负责人就会惧怕改变,这将直接影响服务质量和用户体验,最终还是害了产品。
此外,作为开发者应该时常思考如何量化服务的质量。是不是满足了一定的服务质量指标就好了?还有没有其他指标?在不同阶段这些问题的答案都不一样,比如已有服务基本稳定之后,开发者可能会从最初的服务质量转而关注如何发现服务性能瓶颈上,这时,难以运维(或者说“监控”)的服务就会显示出弊端,甚至变得没法满足运维体验方面的需求,导致重新开发。
所以在代码设计完了之后就开始考虑如何运维它是一个很值得建议的做法。
最后,好用的产品要落实到用户身上,他们的用户体验是绝对产品成败的关键。在这个时候,开发者可能有的误区是追求完美,希望在项目结束的时候就解决所有已经发现的问题,这其实完全没必要。很多大软件(例如微软的各种软件)遗留一堆bug就敢发布,为什么,就是因为开发者应该追求的是用户体验,而不是那些完全不影响用户体验、只有geek可以发现的微小缺陷。
另外,有这种追求完美思想的另一个原因是惧怕变化,希望一次性做完之后再也不去碰这份完美的代码,实际上这也是不切实际的。保持重构才是我们应该追求的目标,无论是针对代码本身,还是各种体验。
我们在某些时候或许可以追求快而放弃一个或两个体验而直接追求用户体验,但最后还是要补课,欠下的总要还。同时,不要一次性就把三种体验追求到极致,它们应该是迭代完成的。
要想一开始就能做到较好的开发体验、运维体验直到用户体验其实并不难,难就难在有没有这样去理解这些事情。说白了,就是有没有在各个环节为他人着想、为未来打基础,这才是一种做产品的开发者。
C2C卖家和个体户
我总是很自然的把C2C卖家和个体户联系起来。
我还记得90年代初,个体户遍布大街的景象。当时不少没文化也没资本的小户人家“下海”变成个体户,在大街上免费占道,做起买卖。武汉现在最繁华的江汉路步行街,在当时就是个体户一条街,一到晚上,不知道从哪里冒出来的个体户们搭起小棚子卖一些便宜的日用品。
有了个体户,实际上也就是有了另一种消费渠道。原来必须去商场或者杂货铺买的东西,如果门口的个体户可以解决,自然就近买了。原来在大商场买不到的杂牌小玩意也因为个体户的出现变得普及,使得消费有了更多的选择。
这些小商贩之所以被称作“个体户”是因为他们一般都是一个人或一家人支撑一个小店,没有雇员,一般也不去缴税。经过不断发展,少数个体户抓住一些机遇变成了大户,慢慢成了合法的公司,也不再流浪街头,有了自己的地盘。还有不少个体户,因为利润薄、自己又没什么文化不懂经商,一直只能保本经营,没太大起色。
随着时代的发展,那些保本的个体户越发活不下去了。首先是马路不再免费,警察/城管把不愿意交钱的个体户赶走,断了他们的财路。其次,现代的商城和超市出现了,在那里有质量得以保证的商品,而且明码实价,不再需要考虑怎么砍价,省心放心。再次,老城区被开发成商业区,满街都是住在房子里、证照齐全、按时缴税的固定卖家,他们既可能是原来的大商家的分店,也可能是发达了的个体户,他们经营的东西与个体户重叠,直接抢走了个体户的客源。最后,由于商业规则越来越完善,通过个体户方式发迹的可能性越来越小,走向成功需要的知识积累越来越多,明眼的个体户渐渐主动放弃这一行。
现在,个体户这个词已经几乎听不到,他们的后辈,摆着路边摊的小商贩,也经常和假货、骗子、穷人等不太好的词汇绑定在一起,怕是有意踏足商界的人不会再以这种方式打天下了吧。可以说,个体户经过短短十几年的发展到如今已经名存实亡。
当今最受欢迎的卖家是国美、各种Shopping Mall、洋百货,他们为什么会成功我不知道,但我大概可以确定个体户包括与他们类似的C2C卖家不会再卷土重来了。面对大商家,那些小鱼小虾们无论在资金还是渠道还是物流方面都处于劣势,他们还要面对很大的政策风险(以后会出现网络城管么?)。他们一定会存在,因为有需求,但很难成为主流。显然,想从他们身上捞取运营费用将极为困难,有钱的话还是自己做商场比较好。
Opera Unite:怎么看这个玩意也不会成功
标题说的是直白了点,不过是心里话。使用Opera Unite以后,真的觉得这个公司的努力怕是又要打水漂了。
首先,Opera Unite还在玩Web 2.0概念,这其实已经有点过时,粘度有限。特别是Opera的用户还属于小众、互动的圈子不够大的情况下,Unite的新鲜劲过去以后,使用者的孤独感会逐渐产生,最后就会使得Unite的互动圈子进一步萎缩、甚至死亡。
其次,Unite本身其实与浏览器关系不大,它更像是Opera的插件平台,能帮用户完成很多与浏览器无关的事情,如果Opera把这个当作它的killer feature,那恐怕就押错宝了。从Unite的扩展性上看,它无法与Firefox的插件体系相比。从功能上看,Flock这样的社会化浏览器就能在功能上轻松仿制Unite。
最后,Opera Unite与手机和其它设备互动的想法很好但很难实现。技术难度是显而易见的,在客户端上面动脑筋实在比Web App难很多。Opera这个公司能有这么大的号召力去推动大家在Unite平台上写应用么?如果没有,他们自己有实力搞定那么多个平台么、写出那么多引人入胜的应用?这个非常值得怀疑。同时,Unite在前期是不会有什么盈利模式的,Opera必须持续砸钱供养整个团队,并且做很多必要的宣传,这种运营成本肯定不低。那么困难同时还得不断花钱,Opera公司是否能够一直坚持也值得观察。
Opera公司10年来占有率一直在1%上下徘徊,他们的营销能力实在令人怀疑。我对他们这次放的这颗卫星不抱太多希望,静观其变,静观其慢慢消亡。
