<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Liu Longjun's Blog]]></title>
  <link href="http://longjun2013.github.io/atom.xml" rel="self"/>
  <link href="http://longjun2013.github.io/"/>
  <updated>2013-04-28T15:10:57+10:00</updated>
  <id>http://longjun2013.github.io/</id>
  <author>
    <name><![CDATA[Longjun]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[使用Maven工具让Web应用一键运行Unit Tests和Functional Tests]]></title>
    <link href="http://longjun2013.github.io/blog/2013/04/27/run-unit-tests-and-functional-tests-together-with-maven/"/>
    <updated>2013-04-27T23:40:00+10:00</updated>
    <id>http://longjun2013.github.io/blog/2013/04/27/run-unit-tests-and-functional-tests-together-with-maven</id>
    <content type="html"><![CDATA[<p>最近在做Web项目开发，使用AngularJS。一开始不知道怎么做测试，后来，在<a href="http://www.yearofmoo.com/2013/01/full-spectrum-testing-with-angularjs-and-testacular.html">这个页面</a>里了解了很多AngularJS的单元测试和功能测试(End to End测试)技巧。 因此，在项目中，我们使用<a href="http://karma-runner.github.io/0.8/index.html">Karma</a>(之前叫做Testacular)来运行Javascript的单元测试，也用Karma来运行AngularJS的e2e测试。</p>

<p>接着，我们写了两个Karma的配置文件，一个是单元测试的，一个是e2e测试的。我们可以用Karma命令单独运行这两种测试，但是我们又希望能够把这两个测试集成到应用的构建脚本中，以便我们运行</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ mvn clean install</span></code></pre></td></tr></table></div></figure>


<p>时，就会运行这两种测试，只要有其中一种测试失败，构建就失败。</p>

<p>使用<a href="https://github.com/karma-runner/maven-karma-plugin">Karma Maven Plugin</a>可以很轻松地配置单元测试和e2e测试。</p>

<p>配置如下：</p>

<!-- more -->




<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'><span class="nt">&lt;plugin&gt;</span>
</span><span class='line'>  <span class="nt">&lt;groupId&gt;</span>com.kelveden<span class="nt">&lt;/groupId&gt;</span>
</span><span class='line'>  <span class="nt">&lt;artifactId&gt;</span>maven-karma-plugin<span class="nt">&lt;/artifactId&gt;</span>
</span><span class='line'>  <span class="nt">&lt;version&gt;</span>1.0<span class="nt">&lt;/version&gt;</span>
</span><span class='line'>  <span class="nt">&lt;executions&gt;</span>
</span><span class='line'>      <span class="nt">&lt;execution&gt;</span>
</span><span class='line'>          <span class="nt">&lt;id&gt;</span>unit-test<span class="nt">&lt;/id&gt;</span>
</span><span class='line'>          <span class="nt">&lt;phase&gt;</span>test<span class="nt">&lt;/phase&gt;</span>
</span><span class='line'>            <span class="nt">&lt;goals&gt;</span>
</span><span class='line'>              <span class="nt">&lt;goal&gt;</span>start<span class="nt">&lt;/goal&gt;</span>
</span><span class='line'>            <span class="nt">&lt;/goals&gt;</span>
</span><span class='line'>          <span class="nt">&lt;configuration&gt;</span>
</span><span class='line'>              <span class="nt">&lt;configFile&gt;</span>/path/to/karma.conf.unit.js<span class="nt">&lt;/configFile&gt;</span>
</span><span class='line'>          <span class="nt">&lt;/configuration&gt;</span>
</span><span class='line'>        <span class="nt">&lt;/execution&gt;</span>
</span><span class='line'>        <span class="nt">&lt;execution&gt;</span>
</span><span class='line'>          <span class="nt">&lt;id&gt;</span>e2e-test<span class="nt">&lt;/id&gt;</span>
</span><span class='line'>          <span class="nt">&lt;phase&gt;</span>integration-test<span class="nt">&lt;/phase&gt;</span>
</span><span class='line'>            <span class="nt">&lt;goals&gt;</span>
</span><span class='line'>                <span class="nt">&lt;goal&gt;</span>start<span class="nt">&lt;/goal&gt;</span>
</span><span class='line'>            <span class="nt">&lt;/goals&gt;</span>
</span><span class='line'>          <span class="nt">&lt;configuration&gt;</span>
</span><span class='line'>              <span class="nt">&lt;configFile&gt;</span>/path/to/karma.conf.e2e.js<span class="nt">&lt;/configFile&gt;</span>
</span><span class='line'>          <span class="nt">&lt;/configuration&gt;</span>
</span><span class='line'>        <span class="nt">&lt;/execution&gt;</span>
</span><span class='line'>    <span class="nt">&lt;/executions&gt;</span>
</span><span class='line'><span class="nt">&lt;/plugin&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>以上代码指定了单元测试和功能测试分别在Maven的Life cycle的<code>test</code>和<code>integration-test</code>阶段运行。</p>

<p>好了，当我们运行</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ mvn clean install</span></code></pre></td></tr></table></div></figure>


<p>时，单元测试可以正常运行，但是e2e测试却失败了。原来，AngularJS的e2e测试需要启动Web服务器，访问真实的URL才行，否则e2e测试就没有意义了呀。
由于我们还没有配置Web服务器，所以e2e测试就失败了。</p>

<p>那么，接下来就是配置Jetty服务器，让Jetty服务器在运行<code>integration-test</code>之前启动，在<code>integration-test</code>运行完成之后关闭就好了。同样，我们要用<a href="http://docs.codehaus.org/display/JETTY/Maven+Jetty+Plugin">Jetty Maven Plugin</a>来达到目的。</p>

<p>下面是Jetty的配置：</p>

<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'><span class="nt">&lt;plugin&gt;</span>
</span><span class='line'>        <span class="nt">&lt;groupId&gt;</span>org.mortbay.jetty<span class="nt">&lt;/groupId&gt;</span>
</span><span class='line'>        <span class="nt">&lt;artifactId&gt;</span>maven-jetty-plugin<span class="nt">&lt;/artifactId&gt;</span>
</span><span class='line'>        <span class="nt">&lt;version&gt;</span>6.1.10<span class="nt">&lt;/version&gt;</span>
</span><span class='line'>        <span class="nt">&lt;configuration&gt;</span>
</span><span class='line'>                <span class="nt">&lt;stopKey&gt;</span>foo<span class="nt">&lt;/stopKey&gt;</span>
</span><span class='line'>                <span class="nt">&lt;stopPort&gt;</span>9999<span class="nt">&lt;/stopPort&gt;</span>
</span><span class='line'>        <span class="nt">&lt;/configuration&gt;</span>
</span><span class='line'>        <span class="nt">&lt;executions&gt;</span>
</span><span class='line'>                <span class="nt">&lt;execution&gt;</span>
</span><span class='line'>                        <span class="nt">&lt;id&gt;</span>start-jetty<span class="nt">&lt;/id&gt;</span>
</span><span class='line'>                        <span class="nt">&lt;phase&gt;</span>pre-integration-test<span class="nt">&lt;/phase&gt;</span>
</span><span class='line'>                        <span class="nt">&lt;goals&gt;</span>
</span><span class='line'>                                <span class="nt">&lt;goal&gt;</span>run<span class="nt">&lt;/goal&gt;</span>
</span><span class='line'>                        <span class="nt">&lt;/goals&gt;</span>
</span><span class='line'>                <span class="nt">&lt;/execution&gt;</span>
</span><span class='line'>                <span class="nt">&lt;execution&gt;</span>
</span><span class='line'>                        <span class="nt">&lt;id&gt;</span>stop-jetty<span class="nt">&lt;/id&gt;</span>
</span><span class='line'>                        <span class="nt">&lt;phase&gt;</span>post-integration-test<span class="nt">&lt;/phase&gt;</span>
</span><span class='line'>                        <span class="nt">&lt;goals&gt;</span>
</span><span class='line'>                                <span class="nt">&lt;goal&gt;</span>stop<span class="nt">&lt;/goal&gt;</span>
</span><span class='line'>                        <span class="nt">&lt;/goals&gt;</span>
</span><span class='line'>                <span class="nt">&lt;/execution&gt;</span>
</span><span class='line'>        <span class="nt">&lt;/executions&gt;</span>
</span><span class='line'><span class="nt">&lt;/plugin&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>如上面的代码所示，Jetty会在Maven的Life Cycle中的<code>pre-integration-test</code>阶段启动，在<code>post-integration-test</code>阶段关闭。</p>

<p>好了，这下运行</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>mvn clean install</span></code></pre></td></tr></table></div></figure>


<p>会先运行Unit Tests，再启动Jetty，然后再运行e2e测试，最后关闭Jetty服务器。</p>

<p>完美了。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[《可操作的民主》读后感——罗伯特议事规则]]></title>
    <link href="http://longjun2013.github.io/blog/2012/12/20/roberts-rules-of-order/"/>
    <updated>2012-12-20T21:17:00+11:00</updated>
    <id>http://longjun2013.github.io/blog/2012/12/20/roberts-rules-of-order</id>
    <content type="html"><![CDATA[<p><img class="left" src="http://longjun2013.github.io/images/2012/12/roberts-rules-cover.jpg" width="140" title="可操作的民主" alt="书的封面">
这本书很薄，讲述的是这么一个有趣的故事。
在安徽阜阳南塘村合作社，土生土长的合作社领袖苦恼于合作社的治理问题，其中包括合作社开会过程中存在的“跑题”、“一言堂”、“野蛮争论”等问题。于是结合几名异端者，把“罗伯特议事规则” 带给合作社的村民们，让“罗伯特议事规则”来指导大家，开高效的会议。
大家可以想象村民们的文化水平，要想把“罗伯特议事规则”，这种高度抽象化和规则化的东西教给他们，是多么的困难，也是多么的有意思。
于是，很多有趣的故事就在书中一一展开。</p>

<!-- more -->


<h2>规则简介</h2>

<p>“罗伯特议事规则”是美国最广受承认的议事规则典范，于1876年由美国人亨利·罗伯特将军以英美议会议事规则为基础、结合民间需求而推出。
它是在竞争环境中为公正平衡和正当维护参与方的利益而设计的会议工具，以人们的常识为基础，成功地将法制、民主、权利保护、权力制衡、自由与制约、效率与公平等理念融汇在具体的执行细节中。
正如罗伯特本人所言：“要让强势一方懂得他们应该让弱势一方有机会自由完整地表达自己的意见，而让弱势一方明白既然他们的意见不占多数，就应该体面地让步，把对方的观点作为全体的决定来承认，积极地去参与实施，同时，他们仍有权利通过规则来改变局势。”</p>

<h2>有意思的几点</h2>

<blockquote><p><em>“跑题、一言堂、野蛮争论怎么办”</em></p></blockquote>

<p>会议设有“主持人”角色，主持人保持主持中立原则，不发表意见，不参与讨论。主持人可以随时打断跑题发言，可以限制每人的发言次数等。</p>

<blockquote><p><em>“什么样的话题才能拿到会议上来讨论？”</em></p></blockquote>

<p>为了避免会议没有Action产出，罗伯特议事规则对提交的话题有严格的要求，即动议可行规则。那就是，话题必须是一个动议、动议，就是行动的建议，是具体的、明确的、可操作的行动建议。这个动议包括，时间、地点、人物、行动、资源、结果六个要素。</p>

<p>拿到会上的应该是一个“怎么干”的东西，然后，在会议上，主持人带领大家一起完善这个动议。</p>

<blockquote><p><em>“怀疑别人的动机？”</em></p></blockquote>

<p>“罗伯特议事规则”里有一条是不许质疑动机——不能以道德的名义去怀疑别人的动机。为什么呢，规则的解释是，“一来动机是不可证实的东西；二来会议要审议的不是某个人，而是某件事，对动机的怀疑和揭露本身就是对议题的偏离；第三，利己性是人类共有的本性，在不侵害他人和社会利益的前提下，追求利益最大化并不为过，所以指责他人的动机毫无意义，不仅不能解决问题，反而增加矛盾。”</p>

<h2>结尾</h2>

<p>“罗伯特议事规则”是最著名的政治辩论规则，但它毕竟是政治辩论规则，我们的日常工作会议不一定都适用，但是，可以借鉴参考的地方有很多噢。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[我们这样去追求卓越的软件（二）—— 如何进行自动化测试和手工测试]]></title>
    <link href="http://longjun2013.github.io/blog/2012/06/01/how-to-do-automated-and-manual-tests/"/>
    <updated>2012-06-01T22:28:00+10:00</updated>
    <id>http://longjun2013.github.io/blog/2012/06/01/how-to-do-automated-and-manual-tests</id>
    <content type="html"><![CDATA[<p>接着我的上一篇文章《我们这样去追求卓越的软件（一）—— 如何保持代码风格统一》，我想在这篇文章里分享一下我们进行自动化测试和手工测试的一些具体实践和操作步骤。</p>

<p>本来早就想写这篇文章了，可是一直没有时间。最近刚来到成都，事情总是很多，太忙了也懒得写了。不过想想这都7月了，上一篇文章还是4月写的，中间间隔太长了，不能停，还是得坚持写下去。于是今天无论如何要完成这篇文章。</p>

<p>阅读提示：文章标题说我们是这样去追求卓越的软件，并不意味着我们这样就已经是卓越的团队了。卓越无止境，在精益求精的道路上，我们一直在努力。</p>

<p>首先抽象地描述一下项目背景，这个项目是一个面向消费者的Web系统（以下简称系统A）。用户访问系统A，输入数据，系统A 接收数据，然后调用系统B 的REST接口返回处理过的数据给用户。其中系统B 是由另一个团队开发和维护的。描述地够抽象的吧，不过你可以想象，比如一个电商网站。</p>

<p>该项目采用Java，框架是Spring，构建工具是Maven，技术不算很新啦。</p>

<p>好了，要说到<strong>自动化测试</strong>，肯定得先说说我们是如何按照需求进行开发的。</p>

<!-- more -->


<p>首先，我们不是按照一份全面的12页的需求说明文档来开发，那样的话，无休止的前期的设计讨论会、数据库设计、代码框架设计、架构讨论会，再加上编码和测试的时间，等全部功能都开发出来都已经是一个月以后了。对我们这个面向消费者的互联网应用来说，一个月太长了。等到一个月以后，这些一个月以前收集的需求和实现的功能，对消费者还有没有价值，都不一定呢。</p>

<p>我们的工作是价值拉动的，所以我们要快速响应变化，我们的哲学是：“先把应用跑起来，完成一小部分<strong>可以使用</strong>的业务功能，看看<strong>反馈</strong>再说”。</p>

<p>所以我们按照<strong>故事点</strong>来划分需求，比如这三天我们就开发”登录功能“，这里面包括页面的登录框，页面JS校验，后台逻辑校验，数据库表字段调整，调用系统B 的验证接口等。这样，三天以后，团队就能给产品经理<strong>/Boss</strong>演示“登录功能”，接受产品经理<strong>/Boss</strong>的反馈，并在第四天就开始改进，第五天就可以再给产品经理<strong>/Boss</strong>演示改进后的“登录功能”了。</p>

<p>而不像传统的软件开发方式，团队跟产品经理/Boss说，“一个月以后你才能看到全部功能中的登录功能，到时候你要是不满意，走需求变更流程，需要再等一个月！”。</p>

<p>那么，这个“登录功能”包括页面的登录框，页面JS校验，后台逻辑校验，数据库表字段调整，调用系统B 的验证接口，这些都是可以自动化验证的。页面的登录框我们用<a href="http://docs.seleniumhq.org">Selenium</a>验证，JS校验逻辑我们用<a href="http://pivotal.github.io/jasmine/">Jasmine</a>验证，后台逻辑校验我们用<a href="http://www.junit.org">JUnit</a>验证，数据库表字段调整，我们用<a href="http://www.junit.org">DBUnit</a>验证，调用系统B 的接口，我们用<a href="http://code.google.com/p/mockito/">Mockito</a>来mock接口调用验证。</p>

<p>当开发人员对测试人员说，我们已经开发完成了“登录功能”，测试人员会问，“如何<strong>证明</strong>你们已经开发完成了登录功能？给我看下你们的自动化测试代码！”</p>

<p>是的，只有通过自动化测试的代码，才能证明功能是正确开发完成的。因为如果没有这些自动化测试，两个星期之后，代码被多处修改，当时写的代码还能不能工作，都不一定呢。</p>

<p>没错，这些自动化测试代码都是开发人员写的。我们的哲学是：<strong>&#8220;You build it, You test it!&#8221;</strong></p>

<p>看到这里，你可能会问，Selenium 验证，Jasmine 验证，JUnit验证， DBUnit和 Mockito 验证，写了这么多的自动化测试代码，该怎么运行呢？</p>

<p>没问题，我们的构建工具是用的Maven嘛，这些测试都有对应的<a href="http://maven.apache.org/plugins/index.html">Maven插件</a>，我们把这些测试都集成到了Maven的构建脚本里，这样，在本地命令行，仅仅运行 “mvn clean install”  就能挨个运行这些测试了，当运行到Selenium测试时，我们的脚本还会在本地把Jetty服务器运行起来，打开本地的Firefox进行Web UI测试，运行完毕后退出Firefox，关闭Jett服务器。如果中途有测试失败的话，脚本就会给出失败提示。</p>

<p>下面是我们项目中实际的自动化测试代码，在powershell中的运行截图：</p>

<p><img src="http://longjun2013.github.io/images/2012/06/junit-result.jpg" title="junit-result" alt="junit-result"></p>

<p>上图是我们项目目前总共的532个JUnit测试运行结果。</p>

<h2>那么测试人员如何做测试？</h2>

<p>开发人员都开始写自动化测试代码了，那么测试人员干什么呢？</p>

<p>我们是这么做的，测试人员负责故事点的测试策略分析，比如“登录功能”有一个要求是：“密码必须包含字母和数字”。</p>

<p>那么测试人员会事先设计至少三个测试用例：</p>

<ol>
<li>密码全部是字母，不通过</li>
<li>密码全部是数字，不通过</li>
<li>密码有部分字母，部分数字，通过</li>
</ol>


<p>当开发人员开始开发“登录功能”时，测试人员会和他说，这里有三个测试用例，你的开发完成时，我需要看到至少三个自动化测试覆盖这三种情况。开发人员说，没问题。</p>

<p>当开发人员开发完毕，测试人员看到了这三个自动化测试用例后，测试人员说，“可以了，我再进行一些登录的手工测试和探索性测试，你给我一个测试环境吧。”</p>

<p>那么开发人员如何给测试人员准备测试环境呢？</p>

<p>这也好办，我们不是有<a href="http://baike.baidu.com/view/5253255.htm">持续集成服务器</a>（以下简称CI）嘛，开发人员提交代码到SVN后，CI检测到有代码提交，会自动运行一遍&#8221;mvn clean install&#8221;,  如果结果是Success，CI会生成一个构建号码，比如“1292”。</p>

<p>开发人员会对测试人员说，“我的功能代码对应的CI构建号是1292，你去把1292号版本发布到测试环境吧。”</p>

<p>这里先说明一下，我们的环境分为DEV，SYS，UAT等。这几个环境的配置大都是一样的，只是给不同的人使用。其中，DEV是给开发人员开发用的，SYS是给测试人员测试用的，UAT是给产品经理和BOSS演示用的。</p>

<p>这时候，就是自动化带来的威力了。</p>

<p><img src="http://longjun2013.github.io/images/2012/06/build-pipeline-to-dev.jpg" title="&#34;build-pipeline-to-dev&#34;" alt="&#34;build-pipeline-to-dev&#34;"></p>

<p>如上图，测试人员访问CI服务器，会看到这样的构建流水线，1292号版本已经在CI上运行通过（第一个绿色），并且被部署到DEV环境中（第二个绿色），SYS和UAT还没有部署1292号版本（蓝色）。</p>

<p>由于我们事先已经在CI上配置好了把代码部署到测试环境的脚本，用Groovy写的，所以测试人员只需要在“1292”号版本的sys环境上点击一个按钮（上图中红色框所示），就能把代码发布到测试环境了。</p>

<p><img src="http://longjun2013.github.io/images/2012/06/build-pipeline-to-uat.jpg" title="&#34;build-pipeline-to-uat&#34;" alt="&#34;build-pipeline-to-uat&#34;"></p>

<p> 上图就是一个已经发布到测试环境的版本。我们也可以用同样的办法发布某个特定版本到UAT环境，随时给BOSS做演示。</p>

<p>几分钟之后，测试人员就开始Happy地进行手工测试和探索性测试了。</p>

<p>新功能发布到测试环境后，如果测试人员发现功能不对，想手工验证下当前测试环境的版本，也是可以的。只需要在浏览器输入“http://应用地址/appcheck.jsp”，就能得到如下页面，</p>

<p><img src="http://longjun2013.github.io/images/2012/06/app-check.jpg" title="&#34;app-check&#34;" alt="&#34;app-check&#34;"></p>

<p>其中的信息包括：应用的maven版本号，CI服务器（我们用的是免费的Jenkins）构建号码，SVN提交版本号，构建时间等信息。</p>

<p>如果测试人员又发现了一些Bug，就会和开发人员一起尝试着再写一些自动化测试代码，以此保证一些重复的验证都由便宜又听话的机器去做，测试人员则抽出他们宝贵的精力来关注测试策略和探索性测试。</p>

<p>好了，三天以后，具有自动化测试代码的“登录功能”OK了，产品经理/Boss看后非常满意，说可以发布到产品环境了。</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[成都新办公室装修完毕——谈谈敏捷团队的工作环境]]></title>
    <link href="http://longjun2013.github.io/blog/2012/05/20/agile-office-decorating/"/>
    <updated>2012-05-20T16:57:00+10:00</updated>
    <id>http://longjun2013.github.io/blog/2012/05/20/agile-office-decorating</id>
    <content type="html"><![CDATA[<p>去年十二月份来到成都时，只是远远地，抬头望着天府软件园E区的楼盘，想象着新的办公室会是个什么样子。由于楼外面还在装修，当时是一片的狼藉。期间，抽空和<a href="http://gigix.thoughtworkers.org">gigix</a>、<a href="http://isaachan.thoughtworkers.org">韩老师</a>在我们的根据地，软件园A区的奇诺咖啡厅一起商讨新办公室的装修方案。我们和好几家装修公司打过交道，有靠谱的，也有不靠谱的。最终在春节前，我们敲定了装修方案。</p>

<p><img src="http://longjun2013.github.io/images/2012/05/out-side-of-office.jpg" title="办公室大楼外面" alt="办公室大楼外面"></p>

<p>过完年，在两位美眉<a href="http://weibo.com/u/1919667517">TW_lifang</a>，<a href="http://weibo.com/u/1919667517">Alice要学习</a>的关照下，办公室就开始华丽丽的装修了。这期间我刚好在悉尼，没有经历装修的过程，两位美眉辛苦了。为了环保材料和一些装修失误等，她们和装修队伍进行了N多次的有效沟通，费时又费力。</p>

<p><img src="http://longjun2013.github.io/images/2012/05/before-decorate.jpg" title="装修之前" alt="装修之前"></p>

<p>四月底，当我们团队从西安回到成都时，已经是焕然一新的办公室啦。</p>

<p>好了，现在切入我这篇文章的主题，结合我们成都办公室的装修经验，谈谈敏捷团队应该需要怎样的工作环境。良好的工作环境，让我们从办公室装修做起。</p>

<!-- more -->


<h2>1. 不吊顶的天花板，Geek品味</h2>

<p><img src="http://longjun2013.github.io/images/2012/05/geek-ceiling.jpg" title="Geek味天花板" alt="Geek味天花板"></p>

<p>流水的管道，风骚的走线。Geek们小时候总喜欢拆收音机啦，拆闹钟啦，见啥好奇的都想拆开看个究竟。如果看到这样不修饰的管道和走线，竟和小时候拆的收音机内部的走线十分相似，不免触发Geek那不经意的灵感和创意。不约束不束缚，我们需要的不是Geek的双手，而是Geek的大脑。</p>

<h2>2. 开放的办公区域，开放的办公桌</h2>

<p><img src="http://longjun2013.github.io/images/2012/05/open-workspace.jpg" title="开放办公环境" alt="开放办公环境"></p>

<p>这种办公桌有一种非常形象的称呼：“敏捷岛”。据说软件园区的管理人员已经把“敏捷岛”，作为园区内软件公司的默认装修样式，我感到非常欣慰。</p>

<p>我们鼓励面对面的沟通，而不是Email，IM来回聊个不停。想象一下，作为开发人员，你左边坐着的是产品经理，右边坐着的是QA，你还需要天天发邮件，用IM沟通吗？在这里，沟通就是靠吼。对产品经理有啥不爽，乱提需求什么的，就吼出来吧，让我们面对面解决问题。</p>

<h2>3. 无处不在的白板和玻璃墙，信息辐射</h2>

<p><img src="http://longjun2013.github.io/images/2012/05/white-board-1.jpg" title="白板" alt="白板">
<img src="http://longjun2013.github.io/images/2012/05/white-board-2.jpg" title="白板" alt="白板"></p>

<p>如果你仔细看上面两张照片，你会发现，这些玻璃墙，都是包裹着建筑物的承重立柱。一般来说，承重立柱会隔断开放的办公区域。但是，如果用玻璃墙把立柱包裹起来，那就是四面都可以使用的白板啦！</p>

<p>我们都知道，Geek的思维一般都很古怪，虽然有想法，但是有时却不能清晰地说出自己的想法（我的中学语文成绩经常在60分左右徘徊。。。） 没关系，那就在玻璃墙上画出来吧，我们通过图画来沟通。不必要先订一个会议室，然后跑到会议室的白板上去沟通（没有会议室就惨了），让我们就地解决，提高效率。</p>

<p>什么，就只有两个立柱的玻璃墙？嫌少了？没关系，看这边，会议室外面，一排排华丽丽的白板墙哦！</p>

<p><img src="http://longjun2013.github.io/images/2012/05/white-board-3.jpg" title="白板" alt="白板"></p>

<h2>4. 会议室的装修方案</h2>

<h3>4.1 会议室一定要有窗户</h3>

<p><img src="http://longjun2013.github.io/images/2012/05/meeting-room-1.jpg" title="会议室" alt="会议室"></p>

<p>会议室是进行头脑风暴的地方，是集思广益的地方，是大脑养分消耗密集的地方，必须提供足够的氧气，开窗通风。一般的公司都喜欢把靠窗的座位装修成领导办公室，但是在这里，没有领导。或者说，大家都是平等的，他们和我们一样，坐在“敏捷岛”里办公，我们把所有靠窗的位置都留给了会议室。</p>

<h3>4.2  会议室的白板一定要更多，更恐怖，要形成信息辐射和环绕效果</h3>

<p><img src="http://longjun2013.github.io/images/2012/05/meeting-room-2.jpg" title="会议室" alt="会议室">
<img src="http://longjun2013.github.io/images/2012/05/meeting-room-3.jpg" title="会议室" alt="会议室"></p>

<p>以上两张图片，不要以为是两个会议室，其实是同一个会议室。所有的玻璃墙都可以随便涂鸦、写字。有时候进入会议室，可以看到其他团队留下的会议结果（这里被我给PS掉了，你懂的）。</p>

<p>也就是说，一次会议结束了，留在那里的就是小组的共同记忆。下次再看到或者别的团队看到，不留神就受到了启发。这就是《视觉会议》环境的三个好处：参与感、全景思维、群体记忆。下面是一个Facebook的会议室，可以作为原型参考。</p>

<p><img src="http://longjun2013.github.io/images/2012/05/facebook-meeting-room.jpg" title="会议室" alt="会议室"></p>

<h3>4.3  会议室的配件一定要齐全</h3>

<p><img src="http://longjun2013.github.io/images/2012/05/items-for-meeting.jpg" title="会议室的配件" alt="会议室的配件"></p>

<p>你有没有到了会议室，却四处找白板笔的经历呢？呵呵，这个盒子里，白板笔，签字笔，铅笔，橡皮，剪刀，胶棒，白卡等，一应俱全。每个会议室都有这样的盒子哦。你也许会问，这些东东要是都用完了怎么办，还不是得四处找行政人员去要？Geek可不喜欢被束缚哦，想找一只白板笔还得四处抓狂。</p>

<p>没关系，看这里:</p>

<p><img src="http://longjun2013.github.io/images/2012/05/all-items.jpg" title="所有物件" alt="所有物件"></p>

<p>所有物件一应俱全。没有锁在柜子里，完全开放给大家使用，各取所需。既节省了行政美眉的管理成本，也方便Geek们快速拿到需要的物件，提高工作效率，一箭双雕。</p>

<h2>5. 人性化设施</h2>

<h3>5.1 书籍借阅</h3>

<p>Geek是最爱学习的了，不能限制他们学习的热情和欲望，公共书籍是不可少的，如下图。</p>

<p><img src="http://longjun2013.github.io/images/2012/05/books.jpg"></p>

<p>什么，书籍一定要锁到柜子里去，走专门的借阅流程？NO，NO，NO。</p>

<p>Geek们不喜欢这么干，等走完该死的借阅流程，Geek看书的热情就已经降低一半了。。。</p>

<p>在这里，一切都靠自觉。想看一本书，自己签个字就行，看完了再还回来就是。</p>

<p><img src="http://longjun2013.github.io/images/2012/05/sign-book.jpg"></p>

<h3>5.2 饮食设施</h3>

<p>Geek们一般都饮食不规律，起得晚，早餐是有一顿没一顿的。而且，很少自己买水果吃，比如我。那么他们的健康也是需要额外的关照。一个小厨房就能解决这样的问题。</p>

<p><img src="http://longjun2013.github.io/images/2012/05/kitchen.jpg"></p>

<p>每天早晨有少量土司面包供应，面包机啥的，已经在送货的路上了。另外，小厨房每天上午10点和下午3点都有新鲜时令水果供应哦。</p>

<h3>5.3 洗浴设施</h3>

<p><img src="http://longjun2013.github.io/images/2012/05/shower.jpg"></p>

<p>Geek们都喜欢运动，中午打打球、健健身啥的，一身臭汗也没办法继续写代码了。不妨在这里冲个澡吧，干干净净、头脑清醒再去写代码噢。</p>

<h3>5.4 办公桌小台灯</h3>

<p><img src="http://longjun2013.github.io/images/2012/05/light.jpg"></p>

<p>有了小台灯，夜晚要是想在公司先打个球，再洗个澡，然后专心地看看书，就不是什么难事了。</p>

<h3>5.5 其他乒乓球设施、游戏设施就不一一列举了。</h3>

<p>好了，看完这些，相信你对敏捷团队的开发环境有了一个大概的了解。</p>

<p>开放的办公区，无处不在的白板和信息辐射，开放、无拘束的文化和人性化的管理，都是为了潜移默化地激励团队，进行创造性的劳动。知识管理的当代，采用传统的管理体力工作者的方法管理知识工作者，已经和这个时代渐行渐远了。</p>

<p>管理知识工作者，让我们首先从办公室装修做起吧。</p>
]]></content>
  </entry>
  
</feed>
