<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
  <channel>
    <title>知之 [&amp;#39;zhi]</title>
    <link>http://blog.afaker.com/</link>
    <description />
    <image>
      <title>知之 [&amp;#39;zhi]</title>
      <url>http://m1.img.libdd.com/farm3/117/3F8783DCB7ED39B729C6F47B781F2B75_64_64.jpg</url>
      <link>http://blog.afaker.com/</link>
      <description />
    </image>
    <item>
      <title>vim 模板</title>
      <link>http://blog.afaker.com/post/2012-05-03/19295912</link>
      <description>&lt;p&gt;http://vim.runpaint.org/typing/inserting-snippets/&lt;/p&gt;&lt;p&gt;写python代码的时候经常会debug调试，因此反复的写import pdb;pdb.set_trace()有时觉得很浪费时间，所以找个vim插件:&lt;span&gt; snip mate，来解决这个问题。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;只需unzip在&lt;a href="http://www.vim.org/scripts/script.php?script_id=2540" target="_blank"&gt;这里&lt;/a&gt;下载下来的文件到~/.vim/目录下即可。&lt;/p&gt;</description>
      <pubDate>Thu, 03 May 2012 08:31:58 GMT</pubDate>
      <guid>http://blog.afaker.com/post/2012-05-03/19295912</guid>
      
    </item>
    <item>
      <title>照片</title>
      <link>http://blog.afaker.com/post/2012-04-29/18122203</link>
      <description>&lt;p&gt;&lt;p&gt;&lt;a href="https://www.postable.com/" target="_blank"&gt;Postable&lt;/a&gt;的创意实在小巧精致，地址簿由联系人自己填写个人信息，省去自己很多时间，而对联系人来说却只需花很少的时间，这也算是&lt;a href="http://en.wikipedia.org/wiki/Crowdsourcing" target="_blank"&gt;Crowdsourcing&lt;/a&gt;了吧。&lt;/p&gt;&lt;/p&gt;
    &lt;p&gt;&lt;img src="http://m1.img.libdd.com/farm3/50/325F6BEEBF6409F01B0194CC66412632_500_586.jpg" /&gt;&lt;/p&gt;</description>
      <pubDate>Sun, 29 Apr 2012 12:49:30 GMT</pubDate>
      <guid>http://blog.afaker.com/post/2012-04-29/18122203</guid>
      
    </item>
    <item>
      <title>照片</title>
      <link>http://blog.afaker.com/post/2012-04-19/18649242</link>
      <description>&lt;p&gt;&lt;blockquote&gt;&lt;p&gt;容忍比自由还更重要 &amp;nbsp;&amp;nbsp;-- 胡适&lt;/p&gt;&lt;/blockquote&gt;&lt;/p&gt;
    &lt;p&gt;&lt;img src="http://m1.img.libdd.com/farm3/146/2EBF8A40D156FA21BE044C870BCD4492_421_600.JPEG" /&gt;&lt;/p&gt;</description>
      <pubDate>Thu, 19 Apr 2012 12:21:44 GMT</pubDate>
      <guid>http://blog.afaker.com/post/2012-04-19/18649242</guid>
      
    </item>
    <item>
      <title>学了个新词BSO：bloody show off</title>
      <link>http://blog.afaker.com/post/2012-04-19/19918072</link>
      <description>&lt;p&gt;http://zhidao.baidu.com/question/57277396&lt;/p&gt;</description>
      <pubDate>Thu, 19 Apr 2012 03:12:43 GMT</pubDate>
      <guid>http://blog.afaker.com/post/2012-04-19/19918072</guid>
      
    </item>
    <item>
      <title>Next Pocket Book To Read: GTD For Hackers</title>
      <link>http://blog.afaker.com/post/2012-04-18/17555115</link>
      <description>&lt;p&gt;http://gtdfh.branchable.com/&lt;/p&gt;</description>
      <pubDate>Wed, 18 Apr 2012 01:49:21 GMT</pubDate>
      <guid>http://blog.afaker.com/post/2012-04-18/17555115</guid>
      
    </item>
    <item>
      <title>为MySQL大表加字段， With Django</title>
      <link>http://blog.afaker.com/post/2012-04-13/18888871</link>
      <description>&lt;p&gt;昨天为一个项目做前奏，给mysql的一张大约有100w条记录的大表加两个字段。很显然，如果直接用alter语句直接添加，会使得mysql锁表大约20分钟，这将是不可接受的。通常的做法是&lt;/p&gt;&lt;ol class="edui-filter-decimal"&gt;&lt;li&gt;&lt;p&gt;创建一张新表new_table，这个新表是有加字段后的schema&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;copy 原始表original_table的数据到新表，新增字段给默认值&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;rename两个表：original_table -&amp;gt; old_table，new_table -&amp;gt; original_table&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;创建新表没有悬念。&lt;/p&gt;&lt;p&gt;copy数据这一步之需要执行一个sql：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;insert into new_table(col1,col2,col3...,new_column1,new_column2) select col1,col2,col3,...,default_value,default_value) from original_table limit offset,limit_num;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;但是值得注意的是一定要加上limit clause，否则后面的这个select语句将读出全部记录，在这个期间会锁表，所以这个sql应该是在一个脚本里遍历执行，预先count下original_table的总记录条数total，利用total，不断的修改offset和limit_num，执行多次。&lt;/p&gt;&lt;p&gt;由于网站是基于Django的，所以可以编写一个command使用django的raw sql来完成这件事。另外在执行完所有sql之后，应当记录下最后一个copy完成的id：lastrowid，下面会用到。&lt;br /&gt;&lt;/p&gt;&lt;p&gt;根据前面记录的最后一条数据的id，编写类似于上面的sql：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;insert into new_table(col1,col2,col3...,new_column1,new_column2) select col1,col2,col3,...,default_value,default_value) from original_table where id &amp;gt;lastrowid ;&lt;br /&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;这条sql的目的是将copy数据sql执行这段时间以及copy数据和下面的rename sql的执行之间的间隔内的新增记录插入的再次copy到new_table里。这个sql需要和rename sql在一个事务里完成，进行短暂的锁表：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;begin;&lt;br /&gt;insert into new_table(col1,col2,col3...,new_column1,new_column2) select col1,col2,col3,...,default_value,default_value) from original_table where id &amp;gt;lastrowid ;&lt;br /&gt;rename table original_table to old_table;&lt;br /&gt;rename table new_table to original_table;&lt;br /&gt;commit;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;附上django使用raw sql复制数据的代码：&lt;br /&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;class Command(BaseCommand):&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;help = 'recreate &amp;nbsp;table'&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;@transaction.commit_on_success&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;def handle(self, *args, **options):&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor = connection.cursor()&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# last id : 3166588 | 2012-04-12 18:22&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;total = 1061492 + 5000&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;size = 1000&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pages = total/size + 1&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for i in range(pages):&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sql = &amp;quot;insert into new_table(col1,col2,col3,new_column1,new_column2) select col1,col2,col3,0,0) from original_table limit %s,%s&amp;quot;&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor.execute (sql, [i * size,size])&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print i,&amp;quot;/&amp;quot;,pages,&amp;quot;------&amp;quot;,cursor.lastrowid,=&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if cursor.lastrowid == 0:&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;transaction.commit_unless_managed()&lt;/p&gt;&lt;/blockquote&gt;</description>
      <pubDate>Fri, 13 Apr 2012 02:58:33 GMT</pubDate>
      <guid>http://blog.afaker.com/post/2012-04-13/18888871</guid>
      
    </item>
    <item>
      <title>Sentry</title>
      <link>http://blog.afaker.com/post/2012-04-12/17128943</link>
      <description>&lt;p&gt;或许你不太会喜欢异常，特别是那些发生后继而沉默在应用日志里那些，你不知道从何开始，因为它们看起来并非那么平易近人，但是用户吵着他的数据有问题，你只得硬着头皮在多个服务器的日志里，翻箱倒柜，试着从堆栈里发现些什么，但是毫无线索，因为你发现这根本是一桩无头命案，没有足够的上下文，不知道哪个才是这个用户的某个操作引起的异常，请求链接更无从谈起，好吧，再去nginx日志里看看吧……但幸运的是，你不是孤独的，&lt;a href="http://disqus.com/" class="edui-filter-decoration-none"&gt;Disqus&lt;/a&gt;也面临着同样的问题，因此而开发了&lt;a href="https://github.com/dcramer/sentry" class="edui-filter-decoration-none"&gt;Sentry&lt;/a&gt;，它是一款精致的Django应用，目的在于帮助开发人员从散落在多个不同服务器上毫无头绪的日志文件里发掘活跃的异常，继而找到潜在的臭虫，当然这个是概述，像其他监控工具一样，sentry也是分为客户端和服务端，客户端分布在你的每个应用服务器上，在异常发生时django会通知它，继而客户端再把详细的异常信息以及相关的用户、请求数据（包括cookie和环境信息）发送到服务端，这样一来就像sentry网站上鼓吹的，它天生就是实时的了。服务端是一个http server，是接收客户端发送异常信息和相关数据，按照异常名称进行归类存储到单独的数据库中，同时提供漂亮的web界面，方便开发检索异常记录，上图：&lt;/p&gt;&lt;p&gt;&lt;a href="https://picasaweb.google.com/100400640714837297010/Blog#5696189927562628834" class="edui-filter-decoration-none"&gt;&lt;span class="text-img-holder"&gt;&lt;img  src="http://m3.img.libdd.com/farm3/84/708564B7A630D53C56C61DEF7C4BD854_400_225.jpg" width="400" height="225" /&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;扇贝部署sentry的当周，开发生活就全部围绕在修复bug中——自己写的代码所产生的异常已经发生xxx次了，就摆在sentry web界面，紧急优先级排在前3名，你还不去修复？现在我们每天都会习惯性的前往sentry看看有没有发生新的异常，发布时更是紧盯着sentry这个晴雨表。这样不但可以让开发时刻关注自己的代码质量，也可以了解到一个异常就有可能是一个用户的流失，尽快地修复一个bug就可以最小化损失。 &amp;nbsp;&lt;/p&gt;&lt;p&gt;如何部署？&lt;/p&gt;&lt;p&gt;服务端： &amp;nbsp;&lt;/p&gt;&lt;p&gt;1. pip install sentry&lt;br /&gt;2. sentry start&lt;/p&gt;&lt;p&gt;客户端：&lt;/p&gt;&lt;p&gt;1. pip install raven&lt;br /&gt;2. 在django的settings.py文件里的INSTALL_APPS加上一行：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;br /&gt;INSTALLED_APPS = [ &amp;nbsp;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;… &amp;nbsp;&amp;nbsp;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# We recommend adding the client to capture errors &amp;nbsp;&amp;nbsp;# seen on this server as well &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;‘raven.contrib.django’,&lt;br /&gt;]&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;br /&gt;3. 将服务端机器上的sentry配置文件中的SENTRY_KEY一行复制到settings.py文件里 &amp;nbsp;&amp;nbsp;&lt;br /&gt;cat ~/.sentry/sentry.conf.py ，复制该行： SENTRY_KEY = xxxxx&lt;br /&gt;4. 指定sentry服务端: &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SENTRY_SERVERS = [‘http://your.sentry.server/sentry/store/’]&lt;/p&gt;</description>
      <pubDate>Thu, 12 Apr 2012 14:03:25 GMT</pubDate>
      <guid>http://blog.afaker.com/post/2012-04-12/17128943</guid>
      
    </item>
    <item>
      <title>Release it中稳定模式的一些理解</title>
      <link>http://blog.afaker.com/post/2012-04-12/18593340</link>
      <description>&lt;span&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;这些模式就像设计模式一样，在我们的系统中不是看你使用了多少模式，或者说是否严格的遵守了这些模式，模式能解决的是大部分通用的情景，但是在实际应用时也要看上下文。&lt;/span&gt;&lt;br /&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Why bother this? 我们系统不是孤立存在，他依赖着很多服务（一个完全不依赖任何资源、服务的系统，几乎是不存在的，当然你可以举出反例，但我觉得这个系统的存在也没有太多意义），很多时候他和这些服务工作的很好但是如果你对他们没有任何防范的话，某天他会让你尝到苦头。&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;在Release it! 一书中举出一些模式以防止某些经常发生的问题再次发生（本书作者提到自己所解决过的线上故障都是新问题，是因为他从不让同一个错误重复发生），的确是经验之谈，这里说下个人的理解。&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;strong&gt;使用超时&lt;/strong&gt;&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;网络通常是不可靠的，我们的应用不能永远等待一个不知道何时才能响应的服务，我们对每个依赖和服务设置超时时间，如果在这个超时时间之内服务还不响应的话，就放弃，让应用继续服务，一方面我们已经尽了最大努力，一方面防止因为无尽的等待以至于因为不断的有新的请求发起导致连接资源耗尽，应用挂住。&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;strong&gt;断路器&lt;/strong&gt;&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;大家都应该知道断路器是怎么工作的，当某个电器因为短路或者其他某些原因使得线路过热而断掉电源，然后可以重新开启电源，使得其他电器正常工作，而你接下来的事情就是检查那个有问题的电器。 同样，我们的系统也是如此，当某一个子系统出现问题或者在几次尝试确定失败之后，应该停止使用这个子系统，使得不会影响整个系统。&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;strong&gt;防水壁&lt;/strong&gt;&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;就像轮船的防水舱一样，我们系统应当在漏水时仍然不会沉船。 防水壁这个我个人感觉和断路器有点类似，但是作者似乎对于防水壁更强调的是物理系统需要分区，在某个物理系统出现问题时，要使得其他物理系统不受影响，以保证整个系统继续工作。而且分区的主要手段就是冗余物理设备。&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;strong&gt;稳定状态&lt;/strong&gt;&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;主要是说尽量不要没事干在线上机器上做些事情，然后举了些常见场景，例如数据清理以及日志文件、内存cache的问题。&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;strong&gt;快速失败&lt;/strong&gt;&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;前面说的使用超时是面向服务消费方，即调用者的建议，而这个主要是面向服务提供方的，如果服务方的系统能够在可能发现失败前，立即回复失败的响应。这样调用方就不会一直等待服务方了（但是在为了保护自己，在不确定服务方是否采用了快速失败的模式，你最好还是设置超时）。但是如何预先知道将会失败呢，作者举了个简单的例子：当一个请求到来时，发现连接池没有可用的连接，应当立即拒绝这个连接请求。&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;strong&gt; 握手&lt;/strong&gt;&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;我们都知道TCP/IP协议的三次握手，主要是用来协调两台设备之间的通信，但是http协议以及RMI，CORBA等等很多通信协议本身都没有做好握手的工作，握手的好处在于一方忙时，另一方能知道他忙，会在后面不再做请求，或者间隔一个设置的时间再作尝试。 幸好看到作者强调了下底层协议最好需要握手，即通信很昂贵的时候，否则个人觉得握手有点鸡肋。&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&lt;strong&gt; Test Harness&lt;/strong&gt;（这个不知道怎么翻译合适）&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;集成测试只是证明在系统规约里明确好的正常情况是否ok，但是无法跳出规约进行测试，test harness是一种接近于边界或者说极端情况下对整个系统进行极限测试的方法，发现系统的漏洞和问题，其目的就像是一名恶意的黑客，极尽所能使得系统崩溃或者发生异常行为。&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;strong&gt;解耦中间件&lt;/strong&gt;&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;从统一进程里编程语言里的方法调用到进程间通信到远程方法调用再到消息队列最终到不同时间不同地点不同进程间的通信，这是一个解耦的过程，越是解耦的设计，越能减少级联失败的发生。&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;我说一下自己曾经就碰到过的问题，准确的说是我制造了这个问题： 在一个项目里我负责编写一个java库以供java前端开发人员访问后台的某个提供http服务的引擎，我使用了HttpClient来请求这个http服务，在这个整个开发过程中，一切都很顺利，然后就交付上线了。不幸的事情发生了，当天晚上前端就匆忙打电话通知我，坏事发生了：日志里显示有大量的读超时异常抛出，线上机器全部被挂住，初始大家以为后台服务有问题导致超时，但是后来dump了下java 堆栈信息，发现大量线程被Block住，查看了代码发现我写的某处有线程安全问题，使得连接并没有正确放回连接池，导致在连接池全部耗尽，新的请求获取不到连接，但是这并不足以使得机器全部挂住，后来一个有经验的开发指出，httpclient连接池默认的获取连接的超时时间是永久等待，这使得不断来到的新请求永久的等待可用的连接，吃不会放弃，等待队列中堆积了成千上万的请求，最终机器挂住。&lt;/p&gt;</description>
      <pubDate>Thu, 12 Apr 2012 14:02:55 GMT</pubDate>
      <guid>http://blog.afaker.com/post/2012-04-12/18593340</guid>
      
    </item>
    <item>
      <title>The best way to complain is to make things.</title>
      <link>http://blog.afaker.com/post/2012-04-12/17132308</link>
      <description />
      <pubDate>Thu, 12 Apr 2012 14:01:17 GMT</pubDate>
      <guid>http://blog.afaker.com/post/2012-04-12/17132308</guid>
      
    </item>
    <item>
      <title>人咬狗</title>
      <link>http://blog.afaker.com/post/2012-04-12/18886669</link>
      <description>&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;我个人是比较喜欢抱怨的，对许多事情不满，有悖于James Murphy说的The best way to complain is to make things.但是我毅然决然stop from making things，而且最近又有人建议我“顺其自然”，那我就随性牢骚一下。&lt;br /&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;看公司每天的纷纷扰扰的邮件总是让人有种世界和平、奥特曼可以退休的感觉，这很好，但我这个人天生有着阴暗心理，唯恐天下不乱，太平和使我不大习惯，但是某天有封邮件使我愕然，这是一封对于公司业务复杂使得代码存在着bug的提议，当然，具体详情我就不透漏了，这里我也罗嗦一下关于业务需求的问题。&lt;br /&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;许多业务需求为了达到某种让用户看起来不空洞、有内涵的内容和特性，而在前台甚至后台做了许多”创口贴”，然后坐等着由于这个需求的上线带来的各种所谓的效果，用各种拼音或者英文混杂的缩写名词术语以及正面反馈的数据来表达这个效果。我不太懂这行水有多深，但是我根据自己的常识以及经验来看，在某个页面上加个颜色花哨的链接或者按钮，而且又放在那么明显的位置，只要是一个四肢健全，知道点击链接不会死人的用户，基本都会好奇的去点一下的，所以哪怕那个链接指向的是一个500 Internal Server Error页面，都会有人点击的。但是你可以想象到在效果数据出来，发出庆功邮件那个弹冠相庆的场面，几近让人感动至落泪。&lt;br /&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;当然其实你搞这么多花样，作为开发其实也管不着，可奇怪的是，当需求上线有问题时会找到开发工程师为啥需求没弄清，当讨论需求时，你提出这个需求太复杂而且技术上不好实现，他们说你照着作就行。其实也无所谓，毕竟直接出错的人是写代码的人，但是呢，如果说你搞这个花样，并没有解决不了用户遇到的问题，而是异想天开认为可能会解决用户的问题的话，就是另一种情况了，当然绝大部分需求都是异想天开，也就是——猜，猜用户喜欢什么，猜这个功能会有用，当然这个也无可厚非，其实也难为这些提需求的人了，他们自己毕竟不是使用阿里巴巴服务的真实用户，怎么会知道真实用户的痛处。我们的 用户也不是作家，他们没法把他们的真实需求表达出来，他们只能说“我找不到我想要的东西”，而这个问题才是最本质的问题，你界面再好看，交互再好，都是浮云，对于用户来说不是很重要，对于公司来说，你要让用户忍受你的界面丑陋但是不得不使用你的服务，当然界面好看最好不过，谁愿意忍受让自己不舒服的的东西呢，但是这些都不是本质问题，本质是内容不好，用户找不到想要的内容，如果这个问题解决不了，我估计“裂缝“会越来越大。所以，不必每天想着怎么在页面上搞点能够让用户点击的东西，也不用天天调整某某数据显示的个数。集中力量把内容质量提升以及内容过滤精确度提高，这才会用户care的。&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;再说技术，像上次轮子一文中提到的那样，技术这块因为业务繁杂的缘故，已经沦为一种每个人都能上的玩物，这不算可悲的，可悲的是它从没有怨言，真是哀其不幸，怒其不争。而每个开发似乎都有种倾向，往项目经理的方向发展（这几乎是必由之路了），都想着能受到赏识，搞个一官半职的，这种开发者典型的特征是喜欢开会，以和每个人搞好关系为工作核心，以和为贵，妥协，从来不对别人说“不“，对技术不思进取，当然这也不能说是坏事，老实说甚至是有追求有理想的有为青年的代表。可是这带来一个极大的问题，这种开发不会拒绝提出有时是荒诞需求的人，因为他需要迎合他，以取得良好的关系，否则当你每次或直接或委婉的拒绝这个需求，指出其不合理时，无论是否你的理由足够充分，关系裂缝就会出现，而这是不被允许的。所以妥协或者麻木成为应用系统的代码复杂，剪不断，理更乱的罪魁祸首。bug丛生，开发周期变长，新员工学习成本大，等等各种问题。然而，是什么导致了这样的问题，我觉得还是文化，没有技术的氛围，以PD为核心的开发模式，当然这也和贵国的世界观有关系：以和为贵。&lt;/p&gt;&lt;p&gt;&lt;span class="text-img-holder"&gt;&lt;img  src="http://m3.img.libdd.com/farm3/154/57F93AFD95F81586E5911F5CC719779A_400_509.jpg" width="400" height="509" /&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <pubDate>Thu, 12 Apr 2012 14:00:41 GMT</pubDate>
      <guid>http://blog.afaker.com/post/2012-04-12/18886669</guid>
      
    </item>
  </channel>
</rss>


