一个建都的计划

前天晚上我们打破了一个世界。面对我们自己给自己设置的边界,我摔了第一个盘子,大家都看到了,知道原来那些桌子架子上的碍着自己事儿的盘子是可以摔的。于是大家有跃跃欲试,准备跟着摔盘子。但问题是,不是摔了盘子就能解决问题的。我们重建的是什么,怎么重建?这是一个计划。

1. 兵分两路。

现在公司需要立刻成立一个小分队,开始筹划重新搭建我们可以依赖的系统。这部分开始只需要很少的人做先锋队。为了速度,我们可以用独立的服务器,新的架构,不考虑和老程序兼容。另外,所有的其他开发和业务,在现有基础上继续推进。原来所有的目标和计划不变。我们的老都城朝歌还要继续存在2年以上,我们需要完善它。这两路兵,对于百姓网都至关重要,我们在变革的时候决不能停下来。

2. 快速推进,长期并存

我们需要快速的推进新都城的建设。我们会在路由器那一层尽快的把第一个页面指向新系统,比如说用户页面。当我们可以看到一个新页面在新的服务器上运行的时候,第一面红旗就已经开始飘扬了。只要有第一个人完成了第一个页面,其他各个页面,比如Listing Page,View Ad Page,Home Page,User Page都可以沿着这条路或快或慢的迁移过去,因为路已经通了。

我们以最高的质量要求建设新页面(code review等流程随之建立),但选择非常小的功能集。所有的没有实现的功能,依然由老的朝歌系统完成。所有的写操作(发帖,付费,信息审核等)都在老朝歌。朝歌作为陪都会一直保留,直到几年后最后一批功能和用户迁移出去。而有一些功能,比如赤壁,牧野以及大多数的管理工具,近期没有改变的计划。

3. 先读后写

这是给技术人员的注释。我们的新都城会先实现读的功能,让主要页面都跑起来。因为读的难度较低,对用户影响最大,对错误容纳度非常高,可以让我们的功能平稳过渡。我们应该可以很快的把80%的流量迁移到新代码上来。当读全部迁移到新代码以后,会逐步小心的把写分步迁移过来。直到全部完成。

我们这几天正在筹划这个新的团队,其实有些工作,在3周前已经开始了。我需要大家这个时候打起精神,在各条线推进;同时要有耐心,罗马不是一天建成的,我们的新都城也不是。

加油!

192行

//wangjianshuo@baixing.com
namespace Graph;

class CompanyAccount {
…function occuredRevenue($startTime, $endTime) {
……$money = 0;
……
……if($endTime > time())
………throw new Exception(“Refuse to tell you future, because that may be inaccurate”);
……
……$s = new Searcher(
………new AndQuery(
…………new Query(‘type’, ‘Order’),
…………new Query(‘paid’, 1),
…………new RangeQuery(‘startTime’, null, $endTime),
…………new RangeQuery(‘endTime’, $startTime – 1, null)
………)
……);
……
……foreach($s as $order)
………$money += ($order->occuredRevenue($endTime) – $order->occuredRevenue($startTime));
………
……return $money;
…}
}

class UserAccount {
…public $balance; // money + credit;
…public $ratio; // money / balance;

…function in($money, $credit){
……$this->ratio = ($this->balance * $this->ratio + $money) / ($this->balance + $money + $credit);
……$this->balance += $money + $credit;
……$this->save();
…}

…function out($mondit) { //mondit = money + credit
……$this->balance -= $mondit;
……$this->save();
…}

…function pay($order) {
……$order->pay($order->listPrice * $this->ratio);
……$this->out($order->listPrice);
…}

…function partialCancel($order, $time) {
……$order->particalCancel($time);
……
……$refund = $order->price – $order->occuredRevenue($time);
……$ratio = $order->price / $order->listPrice;

……$this->in($refund, $refund / $ratio * (1 – $ratio));
…}
}

class Order {
…public $unitTime = 24 * 60 * 60; // 1 day
…public $paid = false;

…function occuredRevenue($time) {
……$money = 0;
……$unitPrice = round(($this->price) / ($this->endTime – $this->startTime) * $this->unitTime, 2);
……for($i = 0; $occuredTime = $this->startTime + $this->unitTime * $i, $occuredTime < min($time, $endTime); $i++)
………if($this->endTime – $this->occuredTime <= $this->unitTime)
…………$money += ($this->price – $unitPrice * $i);
………else
…………$money += $unitPrice;
……return $money;……
…}

…function place() {
……$pa = new BiddingPrice();
……$this->listPrice = $pa->price($order);
……$this->save();
…}

…function partialCancel($time) {
……if($time >= $this->endTime)
………throw new Exception(‘Cannot cancel completed order’);
……
……$neg = clone $this;
……$neg->startTime = max($this->startTime, $time);
……$neg->price = $this->occuredRevenue($time) – $this->price;
……$neg->save();
…}

…function attributes() {
……$ad = graph($this->adId);
……
……return array_merge(
………parent::attributes(),
………’categories’ => cg_array_upto(array_reverse($ad->category->path()), $this->category),
………’areas’ => cg_array_upto(array_reverse($ad->area->path()), $this->area)
…}
}

function cg_array_upto($arr, $c) {
…$result = array();

…foreach($arr as $a) {
……$result[] = $a;
……$if($a == $c)
………break;
…}
…return $result;
}

class DingPrice extends BiddingPrice {
…public $capacity = 12;
…public $sensitivity = 0.5;
…public $bottom = 3;
…public $tolerance = 30; // day
…public $basePrice = 10; // RMB
}

class RefreshPrice extends BiddingPrice {
…public $capacity = 12;
…public $sensitivity = 0.5;
…public $bottom = 3;
…public $tolerance = 30; // day
…public $basePrice = 10; // RMB
}

class BiddingPrice {
…function price($order) {
……$s = new Searcher(
………new AndQuery(
…………new Query(‘type’, ‘Order’),
…………new Query(‘paid’, 1),
…………new Query(‘area’, $order->area),
…………new Query(‘category’, $order->category)
………),
………array(‘order’ => ‘startTime DESC’)
……);
……
……if(count($s) == 0)
………return $this->basePrice;
……
……$currentPrice = $s[0]->listPrice;
……
……if(count($s) > $this->capacity) {
………foreach(array_slice($s, 0 floor($this->sensitivity * $this->capacity) as $o)
…………if($o->listPrice != $currentPrice)
……………return $currentPrice;
………return $this->increase($currentPrice);
……}
……
……if(count($s) <= $this->bottom &&
………time() – $s[0]->startTime > $this->tolerance)
………return $this->decrease($currentPrice);
………
……return $currentPrice;
…}

…function increase($price) {
……return $price + 10;
…}

…function decrease($price) {
……return $price – 10;
…}
}

class DingAds {
…function ads() {
……$v = new Visitor();
……$s = new Searcher(
………new AndQuery(
…………new Query(‘type’, ‘Order’),
…………new Query(‘paid’, 1),
…………new RangQuery(‘startTime’, null, time() + 1),
…………new RangeQuery(‘endTime’, time(), null),
…………new Query(‘categories’, $v->category),
…………new InQuery(‘areas’, $v->area->path())
…………)
………)
……);
……
……$dingAds = array();
……$negAds = array();
……
……foreach($s as $o)
………if($o->price >= 0)
…………$dingAds[] = $o->id;
………else
…………$negAds[] = $o->id;
…………
……return array_diff($dingAds, $negAds);
…}
}
/**
* 1. AccuralBasedAccouting DONE
* 2. Ding (Category, Filter, City, Province, China) DONE
* 3. Place order, pay, refund, partialCancel DONE
* 4. Pricing (Ding bidding based pricing, Refresh bidding, list bidding) DONE
* 5. User Account, real money, fake money DONE
* 6. Ding display DONE
* 7) Refresh 5 RMB
* 8) listing fee 10 RMB
* 9) Sales tools
* 10) Automatic invoice
* 11) Port service
*
* o—–o—–o—–o—–ø–
* ^ time
* o—–o—–o—–o—–o—-
*
*……………………………o—–o—–
*
* |——|——|——|——|——|——|——|
* ^ startTime ^ endTime
*
* Order
* =====
* orderId
* starTime
* endTime
* listPrice
* price
* paid
* area
* category
* adId
*
* 1. Order never change after paid.
* 2. Range [startTime, endTime)
* 3. price, money refer to real money,
* 4. listPrice, $credit refer to fake money.
*/

一艘没有人有浆的船

付费是我们众多代码泥潭的一个。5个工程师耗时半年,2万2千行代码,却bug不断,每天都有用户付了钱没置顶,对账对不清,退费消耗整个公司大量的精力,而我们的付费工程师还在抱怨业务过于复杂,对于新增的业务需求,没有人有信心完成。

昨天晚上,我召集所有工程师看我从一个空白页开始一个字一个字的敲代码。把我们大多数现在的业务需求(置顶,竞价,权责发生制记帐,退款等),以及现在还根本没有希望近期支持的业务需求(真钱假钱,刷新竞价等)实现了一遍。代码总共192行,耗时1个小时。

一个192行代码解决的问题,我们花了这么多工程师这么长时间还没有解决!!!这,就是我们的代码质量!这,就是我们的技术?这,就是一家号称技术公司做的事情?!我真想用最脏的话来骂这些该死的代码!

昨晚,我们安静的看着一行一行代码产生的一个小时,其实是我们集体默哀的一个小时。这是对于我们浪费掉的时间,我们浪费掉的机会,我们给所有其他部门带来的痛,以及我们失去的这家技术公司的灵魂,默哀。。。。。

一艘没有人有浆的船

在过去的7年里面,我从来没有觉得公司象今天这样危险,这个公司从来没有象今天这么差,差到真正优秀的,有血性的人,在考虑离开。

大家对于方向迷茫的抱怨由来已久,对于部门之间协作的抱怨也从来没有停止,但我明确的告诉大家,我们现在最大的问题,不是船划向何方的问题,不是如何配合的问题,而是在这条船上的所有人手中,没有一个人拿着浆!

现在的百姓网业务部门的人,只要没法动代码,就几乎什么也做不了。这是互联网公司的特质。

但大家可能不知道的是一个事实是,我们现在的工程师,也根本动不了代码。一个功能工程师不是不给你做,是做不出来。

面对代码的泥潭,没有人敢跳出来整理,只好随之沉下去。我没有说错。我再说一遍,在这个公司里面,工程师现在也没有办法改动我们的代码了。如果一个192行代码可以完成的功能,已经用了2万2千行了,在上面再加上2万2千行,局面只会越来越不可收拾。

现在的核心问题是,在这艘船上,无论业务还是Dev都苦恼得要死,都喊破了嗓子,对于方向达成或者不达成任何一致,我们依然没有看到我们的船向任何一个方向有进展。为什么?因为这艘船上,没有一个人手里面有浆!这就是我们最大的问题!

重建一个有Hacker精神的团队

大家看到我从3周前开始阶段性的闭关,看我们的代码,也写了些代码。用一种对自己很残忍的方式把付费的代码敲给大家看, 昨晚也已经是第四次了。我这样做就是为了消除业务人员对代码的恐惧,让工程师看到,除了2万行,还有一个192行的解决方案。我需要让大家的注意力放到,我们可以做什么上面,而不是不能做什么上面。

我需要立刻开始抽人重新构建我们的百姓网基础代码。我需要立刻开始以最高的标准来搭建我们的技术架构,我需要我们的技术能够第一个稳稳的拿起第一把浆,并且逐步把浆交到每一个业务部门的手中。我需要这条船开始启动,哪怕开始是慢的,我们必须起航。

我们的PM们原来多是优秀的Developer,甚至比现在很多Dev的代码写得更漂亮。我们公司有非常多可能的Dev,非常多业务部门的人本来就是工科毕业。花花和丰胸都看得懂代码的大概意思。我们的程序员很多有着非常深邃的对世界的认知,有着创造性的想法,如果愿意并有机会,他们可能比业务更了解用户,了解生意。我们是一家没有边界的Hacker组成的公司,我们会有更多人投入基础的代码工作,我们会让业务部门更直接和我们的Dev工作,让Dev和大家一起动脑子,一起搭建一个伟大的系统。我不信我们真的只能组成瘸子和瞎子互相搀扶才能前行的团队。

一个时代的结束的开始

晓良把我们的付费代码打印出来,贴在了会议室里,让大家永远记住在百姓网的历史上,有这样一段耻辱的时刻。我们知耻而后勇,我们一定能重新站到最棒的技术公司的前沿。我发誓!

昨晚,我说,“今晚不会是一个时代的结束。我们需要一起努力结束它。但今晚一定是一个时代的、结束的、开始。”

这的确会是百姓网的一个糟糕的时代的结束的开始!放心,我们在一起!

写代码这件事

晚上花了两个小时给丰兄和花花展示一次如何写代码,从第一个字符开始,用199行代码完成付费的如下功能:

  1. 权责发生制的记帐
  2. 置顶的产生和显示逻辑
  3. 类目置顶,筛选置顶,全省置顶,全国置顶,区县置顶
  4. 部分订单取消
  5. 置顶竞价,端口竞价
  6. 送天数逻辑,买100送20块这样真钱假钱逻辑

很累。

结论和大家分享:

1. 会代码不等于会逻辑

代码只是一种语言,一种更加精确的语言。会写代码仅仅是会写代码。一个汉语讲不明白话的人用英语也不见得讲明白。一个海明威要是学会了意大利语,估计小说也写得不错。代码仅仅是表达。一个代码写不明白的人,事情一定想不明白。事情想明白的人,就差一点点培训,就能写好的代码。

2. 边界

我们已经在停止学习了。停止学习是创业公司的死亡之日,也几乎是一个人的死亡之日。业务人员为什么不能看代码?我拉丰兄和花花(你没看错)来看代码,而且是看我一个字一个字敲出来,就是用一种残酷的方式打破大家对代码的恐惧。哪有那么复杂?就是一些数学表达式而已。

现在的状态让我感觉警察追小偷,一超过自己的区域,就嘎然而止,看着小偷扬长而去而不敢跨越雷池半步。我们不要讨论边界好不好,讨论一下小偷怎么还逍遥法外的问题好不好?不要骂对面地界的警察无能。骂自己无能,没办法走过去好不好?

3. 跨界

代码逻辑和业务逻辑本来就是在一起的,认为的分在两个人脑子里让谁都想不清。我们怎么可能在一点不知道财务怎么算帐的情况下把帐理清楚呢?怎么可能在不知道代码的情况下把付费逻辑理清楚呢?对不起,我不相信分工,我相信hacker精神,我相信聪明。

最后的底线是,不要告诉我不能做的原因,告诉我怎么可以做。代码理不清楚我来写。199行的代码虽然离上线还有距离,但距离已经不远了。我们的付费复杂吗?要得了两万多行的代码吗?两万行的代码解决问题倒也罢了,还没解决问题。。。。

我又想骂人了。就此打住。。

 

去看大灰机

沪青平公路(也就是318国道)16公里处,正好是虹桥机场跑道的尽头。降落的飞机比上升的要缓慢,速度要慢些,离地的角度也小。如果是一个刮北风的日子,飞机将会从南边降落到跑道,这是看飞机的最好时机。

这样的装置看起来并不起眼,沪青平公路南边有几个,北边更多。但如果你顺着这一字排开的灯塔看过去,会发现它从南到北依次闪过去,唰唰唰的在地上展现出降落的方向。相当壮观。

大灰机来了。起落架已经老早放好,这里的飞机的声音更象汽车,声音很大,轰的一声过去就安静了。不象天上的声音要缭绕很久。

飞机最终快要降落到跑道上了。

每架降落的飞机象喝醉了酒一般。

  1. 机头并不指向跑道,而是歪着(今天东风就偏在东边)螃蟹一样的横着走。且很不稳定,来回摇摆。有个学术名词叫“蟹式滑降”。
  2. 左右机翼一上一下,象钻头一样。
  3. 机头也一抬一降的,好像蛙泳运动员。总之就看一东西在那里来回动,动着动着,一冒烟就着陆了。
这里有个风比较大的时候的降落视频,我看到的虽然没有这么严重,但类似。
整个就是个风筝的感觉。一直动,一直在调整,直到降落。
咳,我又想起来创业公司一直要变就像这个道理。因为风在变,飞机就必须变。如果自己大到这点小风无所谓,倒是可以稳定的朝着既定的方向前行,但如果还没有那么大,还是灵活调整些比较好,就算姿势难看了点。

 

“不公平”是社会进步的原动力

这是一句犯众怒的话。让我先把我要说的不公平和“贪污”,“权力”,“内幕交易”,“践踏法律”等等引发的不公平区分开啦。我指的不公平是在创造财富过程中“谁创造财富,谁保留财富”的原则而引发的不公平。

财富的创造

一个好的科学家,程序员,建筑师,歌手创造的财富很有可能是一个一般人的几千倍,几万倍,而这件事情却不常常被认可。只要他们拥有了财富,我们就有人喊“不公平”,“万恶的社会!”,“贫富分化加大!”。但这种分配的不平等,在我看来恰恰是最公平的,因为谁创造了财富,谁保留它。

我们先说说运动员。比如乔丹。“他为什么为这些事情能拿到5亿美金的财富?”,“为什么每年光工资就有3千万美金?”,“我也打球,为什么还要每场付场地费?”,“如果美国也来一次打土豪分田地,我第一个分的就是乔丹的钱!” 常听见这样的说法。这个区别就是,乔丹在创造财富,而普通的篮球爱好者在消耗财富。

财富是什么?是人们要的东西。有很多人希望看乔丹打球,而其他人没有这样的观众;有很多人愿意买印着乔丹名字的任何东西,比如光Nike的乔丹系列气垫鞋每年都有10亿美金以上的收入(我们可以说乔丹贡献了其中不小的一部分),而没有人会买印着另一个人名字的东西。现代传媒又把这种 “要” 做了杠杠放大。不是一个人要(这是10块钱的财富),也不是100个人要,是几千万的人要看他打球!这几千万就让比另一个球员一点点的好乘以了一个天文数字,这就是财富的总量。他创造的这一切,为什么不让他保留这个财富?

最近我喜欢上Adele的歌,她的歌是我要听的,她的演唱会是我要花钱买票的。这个英国的女孩创造了什么样的价值!别不服,唯一不服的方式是你也让像我一样全球上亿的人喜欢,创造些人们“要的东西”。

财富不是分配的,它是创造的。我们现在已经从农业社会过渡到信息社会了。Adele的歌,乔丹的球并没有让其他人更差,不会让其他人更穷,而仅仅让自己更富。

互联网,电力,蒸汽机,飞机,纺织机,指南针,纸。。。这些历史上的伟大的东西的发明家,他们到底创造了多大的人类财富?在过去的几个世纪里面,他们到底节省了多少无差别人类劳动?他们到底创造了多少人们要的东西?他们可以无私到放弃回报,但我们不能无知到不懂得感谢,不认可他们就是创造财富的人,而且很多靠的是一人之力。

公司的财富分配

公司里面,有创造巨大财富的人,有反抗一切阻力把事情做成的人,有做出来用户真的要的东西的人。这些人在创造财富。

有阻止他们这么做的人,有上班到下班一直在网上逛的人,有不断的制造用户不要的东西的人;他们在拿走用户要的东西,甚至是已经拥有的东西,他们在让用户远离。这些人在摧毁财富。

一个世纪难题就是如何找到不同的人并且给予和创造财富对应的回报。这不是不公平,这是公平。平均主义看起来公平,这是天大的不公平。

在抱怨这个社会公平还是不公平的时候,我们要问一个问题:我们到底给这个社会创造了什么?我们是在创造财富,而仅仅是消耗财富。

(这里有另外一个非常重要的话题,就是所有的人,即使是摧毁财富的人,在社会层面,都应该有权利获得生存的最基本的经济,尊严,安全的保障,对弱者的保护是人类社会发展的基石,这是另外一个话题。)

后注:本文受Paul Graham,Alain de Botton,以及Jane Jacobs的影响,在此表示感谢。

别显摆自己的不专业

有很多人通过让自己在其他领域变得不专业来证明自己在自己的领域专业。 - 潘晓良

曾经发生在PM和Dev身上的故事。一个优秀的开发者,自从变成PM以后,他周围的人就误以为他不会写代码了,而且自己也这样认为。成为PM或许让自己在开发上的进步变慢了,但也不至于一下清零呀。

这个故事在商业和技术中间也在显现。我看到过MBA,讨论的时候把“我不懂技术”当作口头禅,好似这么说别人一下就认为他是商业上的专家了。凭什么一个人不懂技术就比懂技术的人更像是好的商业人才呢?为什么一旦MBA懂了技术,别人就不那么把他的商业能力当回事儿了呢?为什么一个程序员一旦能思考业务问题,就有人觉得他的技术不见的很好呢?

成人的世界这种自我定位和主动屏蔽到了令人发指的地步。有多少人号称自己不会唱歌?有多少人在最近的5年里面没有画过画了?有多少女人听到带电的东西就惊呼:呀,搞不懂搞不懂的!有多少男人通过炫耀自己每切一词苹果都会切到手?这他妈的有什么好显摆的!!!

我们在成长的过程中开始让自己变的专业,但千万不要故意让自己在其他地方不专业。

动手好奇反叛的Hacker精神

这两天正好赶上给办公室新的WIFI的SSID命名。我建议网络名叫hacker(还有一个可以覆盖到整个徐汇交大大草坪的热点,叫Free4Hacker。我花了点时间,写下了我对Hacker精神的理解,给大家瞅瞅。

Hacker不是仅仅在计算机领域才有。历史上的那些杰出的发明家,画家,建筑师,工程师,科学家,很多具有hacker的精神。

他们会发现这个世界里面让他们兴奋的问题,并且自己动手去寻求解决方案。

问题是什么?

Hacker解决的问题可能很大。比如人类想飞而不能飞的问题;比如马的力气不够大的问题;比如把消息尽快的传出去的问题,到在互联网上找不到东西的问题。看看世界上林林总总的解决方案,你会惊讶于莱特兄弟的方案,福特给出的方案,以及Tim-Berners-Lee等很多聪明人给出的方案。Hacker不是以做什么为导向的,他们以解决什么问题为导向。

他们是那些不满足现状的人,是那些长长的排队的队伍里面最想改变流程,让大家不再排队的人;是那些相信做任何事情都有更好的解决方案的人。

自己动手

当发现了问题,他们迫不及待的解决这些问题。如果没有解决方案,他们会自己动手建造解决方案,无论是写代码,还是刨木头,或者买砖头,他们开始干了起来;如果遇到不会干的,他们会学习。

他们没有边界,他们永远不把自己定位为一个程序员,一个建筑师,或者一个业务人员。他们为了解决问题可以带上任何需要的头衔!如果说他们到底身份是什么,他们的身份就是hacker,就是跨越所有边界解决问题的人。他们的好奇心带领着他们进入一个又一个领域,并且都做得很好。

好奇心

他们在做和学中间迅速游走,让其他还在自认为是某个职业的人惊讶的张大嘴巴。曼哈顿计划中的物理学家理查德·費曼就是这样一个人。它不仅物理很强(诺贝尔物理学奖获得者,原子弹的重要贡献者),他还是一个画家,鼓手,甚至在接到妹妹的中文信以后愣是自个儿学习中文,并用中文回了信。换句话讲,那些hacker都是成年了还保留着5岁孩子的好奇心的人,保持开放的心态去学习的人,那些不把自己关在一个地方的人。

反叛精神

Hacker还有特点,就是崇尚自由,打破常规,藐视权威。这或许是中文翻译中“黑客”一词的来源,人权运动和开源软件等运动和Hacker精神有很高的内在相似性,而他们和现代版权保护,政府,边界,平均主义等格格不入。

Hacker要的是解决问题,要的是更好的解决方案。为了这个方案,他们去除一切阻挡的因素。费曼教授最著名的趣事就是为了拿到数据破解了国防部的保险柜。他们反编译源代码,打开任何电子设备的机箱,解剖可以解剖的生物就是为了了解现有方案,了解这个世界是怎么工作的,从而给出更好的方案。所以当他们继续前行的时候一路必然伴随着越界。一个当众嘲笑CEO无知的程序员也常常是那些嘲笑现有的解决方案的程序员,更是那些对于“你做不出来这个东西”的判断最不屑一顾并且证明这个说法是错误的程序员。反抗是Hacker最宝贵的精神,也是现代公司制度中最不受欢迎的精神(虽然大家用更加文雅的词语来教育大家,说要有团队精神)。但我坚决保护这种反叛的种子。

为了把事情完成,他们不等待,他们自己动手,不会就自己做,当看到一点点蛛丝马迹他们会沿着它一路跟下去,在这个过程中不经意的翻了谁的墙,踩了谁的花,这些真的只是副作用而不是他们的本意,甚至有些连Hacker自己都没有注意到。但如果没有他们,我们很少能在技术前沿有所进步。

自己动手+好奇心 + 反叛,这是我能理解的Hacker的精神。我在周围有些人身上看到了这种精神。而我最期待的,就是和Hacker一起工作,并且把Hacker的精神散播出去,让大家成为“疯狂到相信能改变世界的人”,并“最终改变了世界”。

 

看代码的愤怒

刚才花时间看了些代码,怒了。

程序员是思考的人,象画家和作家一样,不是码农,不是通过体力完成功能的人,绝不是!看代码和看书一样,是能感觉到写代码的人的思想。看一篇优美的散文和诗歌,跟看报纸上干巴的公关稿,感觉就是不一样,跟看帐本更不一样。

我刚才看到大量的hardcode的东西,没有逻辑,没有层次,多次拷贝粘贴而成的东西,怒!虽然完成了功能,但根本就是垃圾的代码,是没有灵魂的代码,写这样的东西,真的只能叫码农,不能叫程序员。

代码是有灵气的东西。它是对与世界的抽象。之所以需要有灵气,是因为在复杂的需求和优美的实现中间有一个巨大的矛盾,这个矛盾的解决之道是程序员的能力。

千万不要抱怨现实的复杂。现实必然复杂!画人像的画家从来不应该抱怨模特的脸为什么不长成严格的正方体,好让自己画阴影的时候容易点。试图用正方体去画人像必然失败,会被骂。在现实的复杂和纸面上的优雅之间的巨大冲突,需要由画家的技巧来弥补。

不是复杂的世界一定会导致恶心的代码。精致的代码不见得出来的就是简化得现实。但两者兼得的确来之不易,需要能力!

怎么做到用精致得代码构造复杂的现实呢?

  1. 分层。
  2. 抽象。
  3. 迭代

首先是分层。

举个做菜的例子。中国菜馆的菜单动辄几十种,若是没有合适的分层,连原料存储都成了问题:总不能厨房里面放着几十个格子,每个格子里面放着一样菜的所有原料吧。分层,或许可以分为“配菜层”,“制作层”,“调料层”。

  1. “配菜层”又分成“肉”和“菜”并列的两类。肉里面有牛肉,羊肉,鸡肉等,菜里面几种蔬菜。
  2. “制作层”其实就只有炒,烧,蒸,爆,煮等有限的几种;
  3. “调料层”是放油,盐,辣椒,香菜等等。

分了层,几十种菜就变成可以掌控的几组选择的组合。不管什么菜的肉都放在冷冻箱里,不管什么菜都用有限的几种锅子就做得出来,不管什么菜都是一样的几种辣椒。分了层,就把复杂的东西简化了。好的厨师不是宣布我为了简化只做三种菜,也不会因为几十种菜而需要1000平米的厨房来存货,而是通过分层化繁为简,再由简至繁。

别跟我说复杂,说恶心。线团不缠在线轴上当然复杂,像猫一样在乱麻里面搅和当然很复杂。

第二就是抽象。

数据结构就那几种经典的,设计模式也这么多年没有发现新的了,控制逻辑更是几乎从代码生成到现在无外乎一只手数得过来的。如何在纷繁的世界里面看到后面的逻辑,从Listing页面看到后面可以抽象成collection,复杂的关系抽象出“图”,把相互关联抽象成“边”,把看似毫不相关的东西抽象成一样,把看起来几乎一模一样的东西看透必须是不同的类来表示,这些都是抽象能力。

不努力去抽象,就会平庸的大段拷贝粘贴代码,就只会顺序执行,再加一点 if { } else { },只会字符串拼接而形成HTML,只会无休止的写代码,就只能自己干体力活。不会写循环的程序员小明才会熬夜写:

print(“1”);

print(“2”);

print(“3”);

 print(“100”);

一行一行的写,写呀。。写呀。。写呀。。。写呀。。。写呀。。。!!!一边写还在抱怨:“为什么活儿真他妈的多,我真他妈的苦。”写出来的东西还老多的bug,修bug的时候还不服气:“要不是有这些bug拦着我,我早就写到1000了。。。” 实在是不动脑子加嘴硬的典型代表。

但想想,会了循环和用函数封装,你就比小明高级很多了吗?

最后是迭代。

文章是一点点改出来的,不是写一遍再重写一遍的,不要期待啥时候一个来一个新架构,新框架就能解决问题。前后端分离不是专门的工作,是一种思想,需要立刻操练起来,从自己的每一行新代码开始。

推倒重建在任何领域都不是好主意。因为推倒重建是一种习惯。如果解决问题的办法是大规模重建,在三个月后建成必然会有更多的理由再次推倒建成的,因为这是一种习惯。

也不要等待。程序员不能等待,因为程序是分层的,是可改动的,是可以在一层很乱的情况下封装好把上层做好,之后再更改下层的。工程中的脚手架在代码世界可以用得天衣无缝。必须学会在垃圾堆里建设一个茅屋,在茅屋堆里建大楼。因为除此以外的情形,是不现实的,是追求不到的。

程序会呼吸的。会变化的,会一点点的变好的。看看佛罗伦萨的那些古建筑,450年来,一些建筑里面从泥巴已经变成了不锈钢了,或者表面已经加了防氧化膜了,那个建筑已经不是那个建筑了,却依然美丽。你推倒重建一个大教堂就牛了吗?保持表面不变换结构,或者保持里面不变换外观,土木工程师在现实世界都能做得出来,代码这种思想的东西难道做不出来?

分层,抽象,迭代,就是这些。

希望大家好好看看程序,在自己能心平气和的写出自己觉得漂亮的程序之前别跟别人说自己是程序员了。

提正确的问题,用聪明的办法解决

刚才我们一起过了一下付费,抱歉,我又非常气愤。气愤在于大家的智慧没有发挥,而在一个乱麻中间搅和,完全是在浪费生命。我期望百姓网的程序员都是Hacker,如下是我的期望:

1. 要有追求。

程序员是非常神圣的职业,拥有和建筑师,作家,画家一样的魔力,可以构建一个不存在的世界,而比起那些职业,程序员的工具更多,世界更加绚丽。大家要有追求,成为一名hacker。Hacker的定义是非常聪明的,能够创造世界的程序员。Hacker绝不会有祥林嫂一样的被动的,只知道抱怨的倾向。Hacker会不惜一起代价解决一切问题,没有的回去写,不会的要去学,会像福尔摩斯一样拿到一个线索追问下去直到解决。这就是hacker喜欢开源的原因。我们所有的问题都可以找到根源解决。今天关于置顶的讨论,一个如此简单,4个小时应该就完美解决的东西,被搞成几个人几个月还纠缠不清的一个漩涡,就是因为不敢破,不敢去看搜索,不敢越界,把没有的界限当作自己的界限。Hacker会打破沙锅问到底,Hacker会创造。好的Hacker不会忍受空调过冷而没有行动,更何况是代码这个纯思想的世界。

2. 要问正确的问题。

问正确的问题比解决问题更重要。今天我看到大家问的问题都是如何在Ad上打标签,如何补一个有一个漏洞的问题。我问大家为什么需要在这里纪录,大家说是因为另一个设计。但有没有问如果不是这个设计会是怎样?可能会需要解决的问题更多,或者会更少。但不敢去质疑设计,把自己捆在一个地方,让我感觉到大家在思想的世界里面走动的速度好似一个80岁的老头在现实社会走动的速度,半年过去了还没有走出自己的院子。

3. 要聪明的解决。

大家都是聪明人。问问自己,你的解决办法聪明吗?我们不提代码的事情。代码是思考的一种记录而已。聪明,不是脑子好不好用,而是你是不是真的思考过了。什么叫思考过了?如果对于一个问题,你没有进入过面部没有表情,目光呆滞,梦游一样的在办公室里面踱步的状态,我真不觉得你曾经思考过。思考需要象拼图一样,一些清楚了,先放在那里不要动,去看另外一块,然后再半个小时以后把那个想清楚了,再和这一块拼起来(所以最好我还记得那一块,这就要求没有打断)这种快感是Hacker解决问题的方法。现在的做法不聪明。我看到了苦力的影子。我看到了码农在工作,没有看到思想。代码里面没有思想。告诉大家一个非常简单的判断依据:以百姓网现在的代码复杂度,如果你的大多数函数都超过7行,八成你从来没有思考过问题。

我对于百姓网的程序员抱有极大的期望,现在的代码让我及其失望。我愿意和大家一起花时间来理。

最后加一句:好的程序员需要大量的时间才能磨练出来,需要刻苦。我和小排有一个有趣的讨论,小排说,关于Graph API,我周末花了7个小时,为什么下层还没有理清楚,而你却把上层全写好了?我说:我周末写了8遍,40个小时。

就这么简单。