<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Code Review | 酷 壳 - CoolShell</title>
	<atom:link href="https://coolshell.cn/tag/code-review/feed" rel="self" type="application/rss+xml" />
	<link>https://coolshell.cn</link>
	<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>
	<lastBuildDate>Mon, 06 Jul 2020 09:36:46 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.2</generator>
	<item>
		<title>如何重构“箭头型”代码</title>
		<link>https://coolshell.cn/articles/17757.html</link>
					<comments>https://coolshell.cn/articles/17757.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Wed, 05 Apr 2017 10:07:14 +0000</pubDate>
				<category><![CDATA[C/C++语言]]></category>
		<category><![CDATA[程序设计]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[Code Review]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Refactory]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=17757</guid>

					<description><![CDATA[<p>本文主要起因是，一次在微博上和朋友关于嵌套好几层的if-else语句的代码重构的讨论（微博原文），在微博上大家有各式各样的问题和想法。按道理来说这些都是编程的基...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/17757.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/17757.html">如何重构“箭头型”代码</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>本文主要起因是，一次在微博上和朋友关于嵌套好几层的if-else语句的代码重构的讨论（<a href="http://weibo.com/1401880315/ECmCW0oy2" target="_blank" rel="noopener noreferrer">微博原文</a>），在微博上大家有各式各样的问题和想法。按道理来说这些都是编程的基本功，似乎不太值得写一篇文章，不过我觉得很多东西可以从一个简单的东西出发，到达本质，所以，我觉得有必要在这里写一篇的文章。不一定全对，只希望得到更多的讨论，因为有了更深入的讨论才能进步。</p>
<p>文章有点长，我在文章最后会给出相关的思考和总结陈词，你可以跳到结尾。</p>
<p>所谓箭头型代码，基本上来说就是下面这个图片所示的情况。</p>
<p><img decoding="async" loading="lazy" class="aligncenter wp-image-17758 size-full" src="https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411.jpg" alt="" width="720" height="511" srcset="https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411.jpg 720w, https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411-300x213.jpg 300w, https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411-380x270.jpg 380w" sizes="(max-width: 720px) 100vw, 720px" /></p>
<p>那么，这样“箭头型”的代码有什么问题呢？看上去也挺好看的，有对称美。但是……</p>
<p>关于箭头型代码的问题有如下几个：</p>
<p><span id="more-17757"></span></p>
<p>1）我的显示器不够宽，箭头型代码缩进太狠了，需要我来回拉水平滚动条，这让我在读代码的时候，相当的不舒服。</p>
<p>2）除了宽度外还有长度，有的代码的<code>if-else</code>里的<code>if-else</code>里的<code>if-else</code>的代码太多，读到中间你都不知道中间的代码是经过了什么样的层层检查才来到这里的。</p>
<p>总而言之，<strong>“箭头型代码”如果嵌套太多，代码太长的话，会相当容易让维护代码的人（包括自己）迷失在代码中，因为看到最内层的代码时，你已经不知道前面的那一层一层的条件判断是什么样的，代码是怎么运行到这里的，所以，箭头型代码是非常难以维护和Debug的</strong>。</p>
<h4>微博上的案例 与 Guard Clauses</h4>
<p>OK，我们先来看一下微博上的那个示例，代码量如果再大一点，嵌套再多一点，你很容易会在条件中迷失掉（下面这个示例只是那个“大箭头”下的一个小箭头）</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp">
FOREACH(Ptr&lt;WfExpression&gt;, argument, node-&gt;arguments) {
    int index = manager-&gt;expressionResolvings.Keys().IndexOf(argument.Obj());
    if (index != -1) {
        auto type = manager-&gt;expressionResolvings.Values()[index].type;
        if (! types.Contains(type.Obj())) {
            types.Add(type.Obj());
            if (auto group = type-&gt;GetTypeDescriptor()-&gt;GetMethodGroupByName(L&quot;CastResult&quot;, true)) {
                int count = group-&gt;GetMethodCount();
                for (int i = 0; i &lt; count; i++) { auto method = group-&gt;GetMethod(i);
                    if (method-&gt;IsStatic()) {
                        if (method-&gt;GetParameterCount() == 1 &amp;&amp;
                            method-&gt;GetParameter(0)-&gt;GetType()-&gt;GetTypeDescriptor() == description::GetTypeDescriptor&lt;DescriptableObject&gt;() &amp;&amp;
                            method-&gt;GetReturn()-&gt;GetTypeDescriptor() != description::GetTypeDescriptor&lt;void&gt;() ) {
                            symbol-&gt;typeInfo = CopyTypeInfo(method-&gt;GetReturn());
                            break;
                        }
                    }
                }
            }
        }
    }
}
</pre>
<p>上面这段代码，可以把条件反过来写，然后就可以把箭头型的代码解掉了，重构的代码如下所示：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp">
FOREACH(Ptr&lt;WfExpression&gt;, argument, node-&gt;arguments) {
    int index = manager-&gt;expressionResolvings.Keys().IndexOf(argument.Obj());
    if (index == -1)  continue;
    
    auto type = manager-&gt;expressionResolvings.Values()[index].type;
    if ( types.Contains(type.Obj()))  continue;
    
    types.Add(type.Obj());

    auto group = type-&gt;GetTypeDescriptor()-&gt;GetMethodGroupByName(L&quot;CastResult&quot;, true);
    if  ( ! group ) continue;
 
    int count = group-&gt;GetMethodCount();
    for (int i = 0; i &lt; count; i++) { auto method = group-&gt;GetMethod(i);
        if (! method-&gt;IsStatic()) continue;
       
        if ( method-&gt;GetParameterCount() == 1 &amp;&amp;
               method-&gt;GetParameter(0)-&gt;GetType()-&gt;GetTypeDescriptor() == description::GetTypeDescriptor&lt;DescriptableObject&gt;() &amp;&amp;
               method-&gt;GetReturn()-&gt;GetTypeDescriptor() != description::GetTypeDescriptor&lt;void&gt;() ) {
            symbol-&gt;typeInfo = CopyTypeInfo(method-&gt;GetReturn());
            break;
        }
    }
}
</pre>
<p>这种代码的重构方式叫 <strong>Guard Clauses</strong></p>
<ul>
<li><a href="https://martinfowler.com/" target="_blank" rel="noopener noreferrer">Martin Fowler</a> 的 Refactoring 的网站上有相应的说明《<a href="https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html" target="_blank" rel="noopener noreferrer">Replace Nested Conditional with Guard Clauses</a>》。</li>
</ul>
<ul>
<li><a href="https://blog.codinghorror.com/" target="_blank" rel="noopener noreferrer">Coding Horror</a> 上也有一篇文章讲了这种重构的方式 —— 《<a href="https://blog.codinghorror.com/flattening-arrow-code/" target="_blank" rel="noopener noreferrer">Flattening Arrow Code</a>》</li>
</ul>
<ul>
<li><a href="http://stackoverflow.com/" target="_blank" rel="noopener noreferrer">StackOverflow</a> 上也有相关的问题说了这种方式 —— 《<a href="http://stackoverflow.com/questions/356121/refactor-nested-if-statement-for-clarity" target="_blank" rel="noopener noreferrer">Refactor nested IF statement for clarity</a>》</li>
</ul>
<p>这里的思路其实就是，<strong>让出错的代码先返回，前面把所有的错误判断全判断掉，然后就剩下的就是正常的代码了</strong>。</p>
<h4>抽取成函数</h4>
<p>微博上有些人说，continue 语句破坏了阅读代码的通畅，我觉得他们一定没有好好读这里面的代码，其实，我们可以看到，所有的 if 语句都是在判断是否出错的情况，所以，在维护代码的时候，你可以完全不理会这些 if 语句，因为都是出错处理的，而剩下的代码都是正常的功能代码，反而更容易阅读了。当然，一定有不是上面代码里的这种情况，那么，不用continue ，我们还能不能重构呢？</p>
<p>当然可以，抽成函数：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp">
bool CopyMethodTypeInfo(auto &amp;method, auto &amp;group, auto &amp;symbol) 
{
    if (! method-&gt;IsStatic()) {
        return true;
    }
    if ( method-&gt;GetParameterCount() == 1 &amp;&amp;
           method-&gt;GetParameter(0)-&gt;GetType()-&gt;GetTypeDescriptor() == description::GetTypeDescriptor&lt;DescriptableObject&gt;() &amp;&amp;
           method-&gt;GetReturn()-&gt;GetTypeDescriptor() != description::GetTypeDescriptor&lt;void&gt;() ) {
        symbol-&gt;typeInfo = CopyTypeInfo(method-&gt;GetReturn());
        return false;
    }
    return true;
}

void ExpressionResolvings(auto &amp;manager, auto &amp;argument, auto &amp;symbol) 
{
    int index = manager-&gt;expressionResolvings.Keys().IndexOf(argument.Obj());
    if (index == -1) return;
    
    auto type = manager-&gt;expressionResolvings.Values()[index].type;
    if ( types.Contains(type.Obj())) return;

    types.Add(type.Obj());
    auto group = type-&gt;GetTypeDescriptor()-&gt;GetMethodGroupByName(L&quot;CastResult&quot;, true);
    if  ( ! group ) return;

    int count = group-&gt;GetMethodCount();
    for (int i = 0; i &lt; count; i++) { auto method = group-&gt;GetMethod(i);
        if ( ! CopyMethodTypeInfo(method, group, symbol) ) break;
    }
}

...
...
FOREACH(Ptr&lt;WfExpression&gt;, argument, node-&gt;arguments) {
    ExpressionResolvings(manager, arguments, symbol)
}
...
...
</pre>
<p>你发出现，抽成函数后，代码比之前变得更容易读和更容易维护了。不是吗？</p>
<p>有人说：“如果代码不共享，就不要抽取成函数！”，持有这个观点的人太死读书了。函数是代码的封装或是抽象，并不一定用来作代码共享使用，函数用于屏蔽细节，让其它代码耦合于接口而不是细节实现，这会让我们的代码更为简单，简单的东西都能让人易读也易维护。这才是函数的作用。</p>
<h4>嵌套的 if 外的代码</h4>
<p>微博上还有人问，原来的代码如果在各个 if 语句后还有要执行的代码，那么应该如何重构。比如下面这样的代码。</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp">
//原版
for(....) {
    do_before_cond1()
    if (cond1) {
        do_before_cond2();
        if (cond2) {
            do_before_cond3();
            if (cond3) {
                do_something();
            }
            do_after_cond3();
        }
        do_after_cond2();
    }
    do_after_cond1();
}</pre>
<p>上面这段代码中的那些 <code>do_after_condX()</code> 是无论条件成功与否都要执行的。所以，我们拉平后的代码如下所示：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp">
//重构第一版
for(....) {
    do_before_cond1();
    if ( !cond1 ) {
        do_after_cond1();
        continue
    } 
    do_after_cond1();

    do_before_cond2();
    if ( !cond2 ) { 
        do_after_cond2();
        continue;
    }
    do_after_cond2();

    do_before_cond3();
    if ( !cond3 ) {
        do_after_cond3();
        continue;
    }
    do_after_cond3();

    do_something();  
}</pre>
<p>你会发现，上面的 <code>do_after_condX</code> 出现了两份。<strong>如果 if 语句块中的代码改变了某些<code>do_after_condX</code>依赖的状态，那么这是最终版本。</strong></p>
<p>但是，如果它们之前没有依赖关系的话，根据 DRY 原则，我们就可以只保留一份，那么直接掉到 if 条件前就好了，如下所示：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp">
//重构第二版
for(....) {
    do_before_cond1();
    do_after_cond1();
    if ( !cond1 ) continue;
 
    do_before_cond2();
    do_after_cond2();
    if ( !cond2 ) continue;

    do_before_cond3();
    do_after_cond3();
    if ( !cond3 ) continue;

    do_something();  
}</pre>
<p>此时，你会说，我靠，居然，改变了执行的顺序，把条件放到 <code>do_after_condX()</code> 后面去了。这会不会有问题啊？</p>
<p>其实，你再分析一下之前的代码，你会发现，本来，cond1 是判断 do_before_cond1() 是否出错的，如果有成功了，才会往下执行。而 do_after_cond1() 是无论如何都要执行的。从逻辑上来说，do_after_cond1()其实和do_before_cond1()的执行结果无关，而 cond1 却和是否去执行 do_before_cond2() 相关了。如果我把断行变成下面这样，反而代码逻辑更清楚了。</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp">
//重构第三版
for(....) {

    do_before_cond1();
    do_after_cond1();


    if ( !cond1 ) continue;  // &lt;-- cond1 成了是否做第二个语句块的条件
    do_before_cond2();
    do_after_cond2();

    if ( !cond2 ) continue; // &lt;-- cond2 成了是否做第三个语句块的条件
    do_before_cond3();
    do_after_cond3();

    if ( !cond3 ) continue; //&lt;-- cond3 成了是否做第四个语句块的条件
    do_something(); 
 
}
</pre>
<p>于是乎，在未来维护代码的时候，维护人一眼看上去就明白，代码在什么时候会执行到哪里。 这个时候，你会发现，把这些语句块抽成函数，代码会干净的更多，再重构一版：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp">
//重构第四版
bool do_func3() {
   do_before_cond2();
   do_after_cond2();
   return cond3;
}

bool do_func2() {
   do_before_cond2();
   do_after_cond2();
   return cond2;
}

bool do_func1() {
   do_before_cond1();
   do_after_cond1();
   return cond1;
}

// for-loop 你可以重构成这样
for (...) {
    bool cond = do_func1();
    if (cond) cond = do_func2();
    if (cond) cond = do_func3();
    if (cond) do_something();
}

// for-loop 也可以重构成这样
for (...) {
    if ( ! do_func1() ) continue;
    if ( ! do_func2() ) continue;
    if ( ! do_func3() ) continue;
    do_something();
}
</pre>
<p>上面，我给出了两个版本的for-loop，你喜欢哪个？我喜欢第二个。这个时候，因为for-loop里的代码非常简单，就算你不喜欢 continue ，这样的代码阅读成本已经很低了。</p>
<h4>状态检查嵌套</h4>
<p>接下来，我们再来看另一个示例。下面的代码的伪造了一个场景——把两个人拉到一个一对一的聊天室中，因为要检查双方的状态，所以，代码可能会写成了“箭头型”。</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp">
int ConnectPeer2Peer(Conn *pA, Conn* pB, Manager *manager)
{
    if ( pA-&gt;isConnected() ) {
        manager-&gt;Prepare(pA);
        if ( pB-&gt;isConnected() ) {
            manager-&gt;Prepare(pB);
            if ( manager-&gt;ConnectTogther(pA, pB) ) {
                pA-&gt;Write(&quot;connected&quot;);
                pB-&gt;Write(&quot;connected&quot;);
                return S_OK;
            }else{
                return S_ERROR;
            }

        }else {
            pA-&gt;Write(&quot;Peer is not Ready, waiting...&quot;);
            return S_RETRY;
        }
    }else{
        if ( pB-&gt;isConnected() ) {
            manager-&gt;Prepare();
            pB-&gt;Write(&quot;Peer is not Ready, waiting...&quot;);
            return S_RETRY;
        }else{
            pA-&gt;Close();
            pB-&gt;Close();
            return S_ERROR;
        }
    }
    //Shouldn't be here!
    return S_ERROR;
}</pre>
<p>重构上面的代码，我们可以先分析一下上面的代码，说明了，上面的代码就是对 PeerA 和 PeerB 的两个状态 “连上”， “未连上” 做组合 “状态” （注：实际中的状态应该比这个还要复杂，可能还会有“断开”、“错误”……等等状态）， 于是，我们可以把代码写成下面这样，合并上面的嵌套条件，对于每一种组合都做出判断。这样一来，逻辑就会非常的干净和清楚。</p>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp">
int ConnectPeer2Peer(Conn *pA, Conn* pB, Manager *manager)
{
    if ( pA-&gt;isConnected() ) {
        manager-&gt;Prepare(pA);
    }

    if ( pB-&gt;isConnected() ) {
        manager-&gt;Prepare(pB);
    }

    // pA = YES &amp;&amp; pB = NO
    if (pA-&gt;isConnected() &amp;&amp; ! pB-&gt;isConnected()  ) {
        pA-&gt;Write(&quot;Peer is not Ready, waiting&quot;);
        return S_RETRY;
    // pA = NO &amp;&amp; pB = YES
    }else if ( !pA-&gt;isConnected() &amp;&amp; pB-&gt;isConnected() ) {
        pB-&gt;Write(&quot;Peer is not Ready, waiting&quot;);
        return S_RETRY;
    // pA = YES &amp;&amp; pB = YES
    }else if (pA-&gt;isConnected() &amp;&amp; pB-&gt;isConnected()  ) {
        if ( ! manager-&gt;ConnectTogther(pA, pB) ) {
            return S_ERROR;
        }
        pA-&gt;Write(&quot;connected&quot;);
        pB-&gt;Write(&quot;connected&quot;);
        return S_OK;
    }

    // pA = NO, pB = NO
    pA-&gt;Close();
    pB-&gt;Close();
    return S_ERROR;
}</pre>
<h4>延伸思考</h4>
<p>对于 <code>if-else</code> 语句来说，一般来说，就是检查两件事：<strong>错误</strong> 和 <strong>状态</strong>。</p>
<h5>检查错误</h5>
<p>对于检查错误来说，使用 Guard Clauses 会是一种标准解，但我们还需要注意下面几件事：</p>
<p style="padding-left: 30px;">1）当然，出现错误的时候，还会出现需要释放资源的情况。你可以使用 <code>goto fail;</code> 这样的方式，但是最优雅的方式应该是C++面向对象式的 RAII 方式。</p>
<p style="padding-left: 30px;">2）以错误码返回是一种比较简单的方式，这种方式有很一些问题，比如，如果错误码太多，判断出错的代码会非常复杂，另外，正常的代码和错误的代码会混在一起，影响可读性。所以，在更为高组的语言中，使用 <code>try-catch</code> 异常捕捉的方式，会让代码更为易读一些。</p>
<h5>检查状态</h5>
<p>对于检查状态来说，实际中一定有更为复杂的情况，比如下面几种情况：</p>
<p style="padding-left: 30px;">1）像TCP协议中的两端的状态变化。</p>
<p style="padding-left: 30px;">2）像shell各个命令的命令选项的各种组合。</p>
<p style="padding-left: 30px;">3）像游戏中的状态变化（一棵非常复杂的状态树）。</p>
<p style="padding-left: 30px;">4）像语法分析那样的状态变化。</p>
<p>对于这些复杂的状态变化，其本上来说，你需要先定义一个状态机，或是一个子状态的组合状态的查询表，或是一个状态查询分析树。</p>
<p><strong>写代码时，代码的运行中的控制状态或业务状态是会让你的代码流程变得混乱的一个重要原因，重构“箭头型”代码的一个很重要的工作就是重新梳理和描述这些状态的变迁关系</strong>。</p>
<h4>总结</h4>
<p>好了，下面总结一下，把“箭头型”代码重构掉的几个手段如下：</p>
<p>1）<strong>使用 Guard Clauses </strong>。 尽可能的让出错的先返回， 这样后面就会得到干净的代码。</p>
<p>2）<strong>把条件中的语句块抽取成函数</strong>。 有人说：“如果代码不共享，就不要抽取成函数！”，持有这个观点的人太死读书了。函数是代码的封装或是抽象，并不一定用来作代码共享使用，函数用于屏蔽细节，让其它代码耦合于接口而不是细节实现，这会让我们的代码更为简单，简单的东西都能让人易读也易维护，<strong>写出让人易读易维护的代码才是重构代码的初衷</strong>！</p>
<p>3）<strong>对于出错处理，使用try-catch异常处理和<a href="http://stackoverflow.com/questions/2321511/what-is-meant-by-resource-acquisition-is-initialization-raii" target="_blank" rel="noopener noreferrer">RAII机制</a></strong>。返回码的出错处理有很多问题，比如：A) 返回码可以被忽略，B) 出错处理的代码和正常处理的代码混在一起，C) 造成函数接口污染，比如像atoi()这种错误码和返回值共用的糟糕的函数。</p>
<p>4）<strong>对于多个状态的判断和组合，如果复杂了，可以使用“组合状态表”，或是状态机加Observer的状态订阅的设计模式</strong>。这样的代码即解了耦，也干净简单，同样有很强的扩展性。</p>
<p>5） <strong>重构“箭头型”代码其实是在帮你重新梳理所有的代码和逻辑，这个过程非常值得为之付出</strong>。重新整思路去想尽一切办法简化代码的过程本身就可以让人成长。</p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" id="wp_rp_first"><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/19612.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2019/07/1920px-Margaret_Hamilton_-_restoration-e1563697198766-1-150x150.jpg" alt="50年前的登月程序和程序员有多硬核" width="150" height="150" /></a><a href="https://coolshell.cn/articles/19612.html" class="wp_rp_title">50年前的登月程序和程序员有多硬核</a></li><li ><a href="https://coolshell.cn/articles/17929.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/06/go-hardhat-150x150.png" alt="Go编程模式：修饰器" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17929.html" class="wp_rp_title">Go编程模式：修饰器</a></li><li ><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/29.jpg" alt="Leetcode 编程训练" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12052.html" class="wp_rp_title">Leetcode 编程训练</a></li><li ><a href="https://coolshell.cn/articles/11656.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/06/software_development-150x150.png" alt="开发团队的效率" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11656.html" class="wp_rp_title">开发团队的效率</a></li><li ><a href="https://coolshell.cn/articles/11432.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/04/code_review-150x150.jpg" alt="从Code Review 谈如何做技术" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11432.html" class="wp_rp_title">从Code Review 谈如何做技术</a></li><li ><a href="https://coolshell.cn/articles/11265.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/03/snake-hat-new-year-schedule-800x960-150x150.jpg" alt="Python修饰器的函数式编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11265.html" class="wp_rp_title">Python修饰器的函数式编程</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/17757.html">如何重构“箭头型”代码</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/17757.html/feed</wfw:commentRss>
			<slash:comments>56</slash:comments>
		
		
			</item>
		<item>
		<title>从Code Review 谈如何做技术</title>
		<link>https://coolshell.cn/articles/11432.html</link>
					<comments>https://coolshell.cn/articles/11432.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Sat, 12 Apr 2014 08:28:01 +0000</pubDate>
				<category><![CDATA[技术管理]]></category>
		<category><![CDATA[流程方法]]></category>
		<category><![CDATA[Code Review]]></category>
		<category><![CDATA[Programmer]]></category>
		<category><![CDATA[程序员]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=11432</guid>

					<description><![CDATA[<p>（这篇文章缘由我的微博，我想多说一些，有些杂乱，想到哪写到哪） 这两天，在微博上表达了一下Code Review的重要性。因为翻看了阿里内部的Review Bo...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/11432.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/11432.html">从Code Review 谈如何做技术</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><img decoding="async" loading="lazy" class="alignright size-medium wp-image-11440" alt="" src="https://coolshell.cn/wp-content/uploads/2014/04/code_review-225x300.jpg" width="225" height="300" srcset="https://coolshell.cn/wp-content/uploads/2014/04/code_review-225x300.jpg 225w, https://coolshell.cn/wp-content/uploads/2014/04/code_review-203x270.jpg 203w, https://coolshell.cn/wp-content/uploads/2014/04/code_review.jpg 358w" sizes="(max-width: 225px) 100vw, 225px" />（这篇文章缘由我的微博，我想多说一些，有些杂乱，想到哪写到哪）</p>
<p>这两天，在微博上表达了一下Code Review的重要性。因为翻看了阿里内部的Review Board上的记录，从上面发现Code Review做得好的是一些比较偏技术的团队，而偏业务的技术团队基本上没有看到Code Review的记录。当然，这并不能说没有记录他们就没有做Code Review，于是，我就问了一下以前在业务团队做过的同事有没有Code Review，他告诉我不但没有Code Review，而且他认为Code Review没用，因为：</p>
<p style="padding-left: 30px;">1）工期压得太紧，时间连coding都不够，以上线为目的，</p>
<p style="padding-left: 30px;">2）需求老变，代码的生命周期太短。所以，写好的代码没有任何意义，烂就烂吧，反正与绩效无关。</p>
<p>我心里非常不认同这样的观点，我觉得我是程序员，我是工程师，就像医生一样，不是把病人医好就好了，还要对病人的长期健康负责。对于常见病，要很快地医好病人很简单，下猛药，大量使用抗生素，好得飞快。但大家都知道，这明显是“饮鸩止渴”、“竭泽而渔”的做法。医生需要有责任心和医德，我也觉得程序员工程师也要有相应的责任心和相应的修养。东西交给我我必需要负责，我觉得这种负责和修养不是”做出来“就了事了，而是要到“做漂亮”这个级别，这就是“山寨”和“工业”的差别。而只以“做出来”为目的标准，我只能以为，这样的做法只不过是“按部就班”的堆砌代码罢了，和劳动密集型的“装配生产线”和“砌砖头”没有什么差别，在这种环境里呆着还不如离开。</p>
<p>老实说，因为去年我在业务团队的时候，我的团队也没有做Code Review，原因是多样的。其中一个重要原因是，我刚来阿里，所以，需要做的是在适应阿里的文化，任何公司都有自己的风格和特点，任何公司的做法都有他的理由和成因，对于我这样的一个初来者，首要的是要适应和观察，不要对团队做太多的改动，跟从、理解和信任是融入的关键。（注：在建北京团队和不要专职的测试人员上我都受到了一些阻力），所以跟着团队走没有玩Code Review。干了一年后，觉得我妥协了很多我以前所坚持的东西，觉得自己的标准在降低，想一想后背拔凉拔凉的，所以我决定坚持，而且还要坚持高标准。</p>
<p><span id="more-11432"></span></p>
<p>对于Code Review很重要的这个观点，在微博上抛出来后，被一些阿里的工程师，架构师/专家，甚至资深架构师批评，我在和他们回复和讨论的过程中，居然发现有个“因为对方用户的设置”我无法回复了（我被拉黑了，还有一些直接就是冷讽和骂人了，微博中我就直接删除了）。这些批评我的阿里工程师/架构师的观点总结一下如下：（<strong>顺便说一下，阿里内还是有很多团队坚持做Code Review的</strong>）</p>
<p style="padding-left: 30px;">1）到业务团队体会一下，倒逼工期的项目有多少？订好交付日期后再要求提前1个月的有多少？现在是做到已经不容易，更不谈做得漂亮！。</p>
<p style="padding-left: 30px;">2）Code Review是一种教条，意义不大，有测试，只要不出错，就可以了。</p>
<p style="padding-left: 30px;">3）目标都是改进质量，有限的投入总希望能有最大的产出，不同沉湎改进质量的方式不一样，业务应用开发忙的跟狗一样，而且业务逻辑变化快，通用性差，codereviw的成本要比底层高。</p>
<p style="padding-left: 30px;"><span style="line-height: 1.5em;">4）现在的主要矛盾是倒排出来的工期和不靠谱的程序员之间的矛盾，我认为cr不是解决这个问题的银弹。不从实际情况出发光打正义的嘴炮实在太过于自慰了 。</span></p>
<p><strong>我们可以看到，上面观点其实和Code Review没有太多关系，其实是在抱怨另外的问题</strong>。这些观点其实是技术团队和业务团队的矛盾，但不知道为什么强加给了我的“Code Review很重要”的这个观点，然后这些观点反过来冲击“Code Reivew”，并说“Code Review无用”。这种讨论问题的方式在很常见，你说A，我说B，本来A、B是两件事，但就是要混为一谈，然后似是而非的用B来证明你的A观点是错的。（也许，这些工程师/架构师心存怨气，需要一个发泄的通道）</p>
<p><strong>我觉得，很多时候，人思考问题思考不清楚，很大一部分原因是因为把很多问题混为一谈</strong>，连我自己有些时候都会这样。引以为戒。</p>
<p>即然被混为一谈，那我就来拆分一下，也是下面这三个问题：</p>
<ul>
<li>Code Review有没有用的问题。</li>
<li>Code Review做不起来的问题。</li>
<li>业务变化快，速度快的问题，技术疲于跟命。</li>
</ul>
<h4>Code Review</h4>
<p>你Google一下Code Reivew这个关键词，你就会发现Code Review的好处基本上是不存在争议的，有很多很多的文章和博文都在说Code Review的重要性，怎么做会更好，而且很多公司在面试过程中会加入“Code Review”的问题。打开<a href="http://zh.wikipedia.org/wiki/%E4%BB%A3%E7%A0%81%E5%AE%A1%E6%9F%A5" target="_blank">Wikipedia的词条</a>你会看到这样的描述——</p>
<blockquote><p>卡珀斯·琼斯（Capers Jones）分析了超过12,000个软件开发项目，其中使用正式代码审查的项目，发现潜在缺陷率约在60-65%之间，若是非正式的代码审查，发现潜在缺陷率不到50%。大部份的测试，发现的潜在缺陷率会在30%左右。</p>
<p>对于一些关键的软件（例如安全关键系统的嵌入式软件），一般的代码审查速度约是一小时150行程序码，一小时审查数百行程序码的审查速度太快，可能无法找到程序中的问题。代码审查一般可以找到及移除约65%的错误，最高可以到85%。</p>
<p>也有研究针对代码审查找到的缺陷类型进行分析。代码审查找到的缺陷中，有75%是和计算机安全隐患有关。对于产品生命周期很长的软件公司而言，代码审查是很有效的工具。</p></blockquote>
<p><strong>Code Review的好处我觉得不用多说了，主要是让你的代码可以更好的组织起来，有更易读，有更高的维护性，同时可以达到知识共享，找到bug只是其中的副产品</strong>。这个东西已经不新鲜了，你上网可以找到很多文章，我就不多说了。就像你写程序要判断错误一样，Code Review也是最基本的常识性的东西。</p>
<p>我从2002年开始就浸泡在严格的Code Review中，我的个人成长和Code Review有很大的关系，如果我的成长过程中没有经历过Code Review这个事，我完全不敢想像。</p>
<p><strong>我个人认为代码有这几种级别：1）可编译，2）可运行，3）可测试，4）可读，5）可维护，6）可重用。通过自动化测试的代码只能达到第3）级，而通过Code Review的代码少会在第4）级甚至更高。</strong>关于Code Review，你可以参看本站的《<a title="Code Review中的几个提示" href="https://coolshell.cn/articles/1302.html" target="_blank">Code Review中的几个提示</a>》</p>
<p>可见，Code Review直接关系到了你的工程能力！</p>
<h4>Code Review 的问题</h4>
<p>有下面几个情况会让你的Code Review没有效果。</p>
<p>首当其冲的是——“<strong>人员能力不足</strong>”，我经历过这样的情况，Code Review的过程中，大家大眼瞪小眼，没有什么好的想法，不知道什么是好的代码，什么是不好的代码。导致Code Review大多数都在代码风格上。今天，我告诉你，代码风格这种事，是每个程序员自查的事情，不应该浪费大家的时间。对此，我有两个建议：1）你团队的人招错了，该换血了。2）让你团队的人花时候阅读一下《<a href="http://book.douban.com/subject/1477390/" target="_blank">代码大全</a>》这本书（当然，还要读很多基础知识的书）。</p>
<p>次当其冲的是——“<strong>结果更重要</strong>”，也就是说，做出来更重要，做漂亮不重要。因为我的KPI和年终奖based on how many works I&#8217;ve done！而不是How perfect they are ! 这让我想到那些天天在用Spring MVC 做CRUD网页的工程师，我承认，他们很熟练。大量的重复劳动。其实，仔细想一下好多东西是可以框架化，模板化，或是自动生成的。所以，为了堆出这么多网页就停地去堆，做的东西是很多，但是没有任何成长。急功近利，也许，你做得多，拿到了不错的年终奖，但是你失去的也多，失去了成为一个卓越工程师的机会。你本来可以让你的月薪在1-2年后翻1-2倍的，但一年后你只拿到了为数不多的年终奖。</p>
<p>然后是——“<strong>人员的态度问题</strong>”，一方面就是懒，不想精益求精，只要干完活交差了事。对此，你更要大力开展Code Review了，让这种人写出来的代码曝光在更多人面前，让他为质量不好的代码蒙羞。另一方面，有人会觉得那是别人的模块，我不懂，也没时间 去懂，不懂他的业务怎么做Code Review? 我只想说，如果你的团队里这样的“各个自扫门前雪”的事越多，那么这个团队也就越没主动性，没有主动性也就越不可能是个好团队，做的东西也不可能好。而对于个人来说，也就越不可能有成长。</p>
<p>接下来是——“<strong>需求变化的问题</strong>”，有人认识，需求变得快，代码的生存周期比较短，不需要好的代码，反正过两天这些代码就会被废弃了。如果是一次性的东西，的确质量不需要太高，反正用了就扔。但是，我觉得多多少少要Review一下这个一次性的烂代码不会影响那些长期在用的代码吧，如果你的项目全部都是临时代码，那么你团队是不是也是一个临时团队？关于如果应对需求变化，你可以看看本站的《<a href="https://coolshell.cn/articles/6950.html" rel="bookmark">需求变化与IoC</a>》《<a href="https://coolshell.cn/articles/7236.html" target="_blank">Unix的设计思想来应对多变的需求</a>》的文章 ，从这些文章中，我相信你可以看到对于需求变化的代码质量需要的更高。</p>
<p>最后是——“<strong>时间不够问题</strong>”，如果是业务逼得紧，让你疲于奔命，那么这不是Code Review好不好问题，这是需求管理和项目管理的问题以及别的非技术的问题。下面我会说。</p>
<p>不管怎么样，上述Code Review的问题不应该成为“Code Review无意义”的理由或借口，这就好像“因噎废食”一样。干什么事都会有困难和问题的，有的人就这样退缩了，但有的人看得到利大于弊，还是去坚持，人与人的不同正在这个地方。这就是为什么运动会受伤，但还是会人去运动，而有人因为怕受伤就退缩了一样。</p>
<h4>被业务逼得太紧</h4>
<p>被业务逼得太紧，需求乱变，这其实和Code Review没有多大关系了。对此，我想先讲一个我的故事。</p>
<p>我去年在阿里的聚石塔，刚去的时候，聚石塔正在做一个很大的重构——对架构的大调整。因此压了很多业务需求，等这个项目花了2-3个月做完了后，一下子涌入了30-50个需求，还规定一个月完成，搞得团队疲于奔命。在累了两周后，我仔细分析了一下这些需求，发现很多需求是在重复做阿里云已经做过的东西，还有一些需求是因为聚石塔这个平台不规范没有标准所产生的问题。于是，我做了这么三件事：</p>
<p style="padding-left: 30px;">1）重新定义聚石塔这个产品主要目标和范围，确定哪些该做，哪些不该做。</p>
<p style="padding-left: 30px;">2）为聚石塔制定标准 ，让阿里云的API都长得基本一样，并制订云资源的接入标准。</p>
<p style="padding-left: 30px;">3）推动重构阿里云的Portal系统，不再实现阿里云已经做过的东西，与阿里云紧密结合。</p>
<p>这些事情推动起来并不容易，聚石塔的业务方一开始也不理解，我和产品一起做业务方的工作，而阿里云也被我逼得很惨（在这里一并感谢，尤其阿里云的同学，老实说，和阿里云跨团队合作中是我这么多年来感觉最好的一次，相当赞）。通过这个事，聚石塔需求一下就有质的下降了。搞得还有几个工程师来和我说，你这么搞，聚石塔就没事可干了。姑且不说工程师对聚石塔的理解是怎么样的。 我只想说，我大量地减少了需求，尽最大可能联合了该联合的人，而不是自己闭门造车，并让产品的目标和方向更明确了。做了这些事情后，大家不但不用加班，而且还有时间充电去学技术，并为聚石塔思考未来的方向和发展。去年公司996的时候，我的团队还在965（搞得跟异教徒似的），而且还有很多时间去专研新的东西。</p>
<p>说这个故事，我不是为了得瑟，而是因为有些人在微博上抨击我是一个道貌岸然的只会谈概念讲道理的装逼犯。所以，我告诉大家我在聚石塔是怎么做的，我公开写在这里，你也可以向相关的同学去求证我说的是不是真的。也向你证明，我可能是个装逼犯，但绝不是只会谈概念讲道理的装逼犯。</p>
<p>被业务方逼得紧不要抱怨，你没有时间被逼得像牲口一样工作，这个时候，你需要的是暂停一下想一想，为什么会像牲口一样？而这正是让你变得聪明的机会。</p>
<p>我为你总结一下，</p>
<p style="padding-left: 30px;">1）你有没有去Review业务部门给你的这么多的需求，哪些是合理的，哪些是不合理的。在Amazon，开发工程师都会被教育拿到需求后一定要问——“为什么要做？业务影响度有多大？有多少用户受益？”，回答不清这个问题，没有数据的支持，就不做。所以，产品经理要做很多数据挖拙和用户调研的工作，而不是拍拍脑袋，听极少数的用户抱怨就要开需求了。</p>
<p style="padding-left: 30px;">2）产品经理也要管理和教育的。你要告诉你的产品经理：“你是一个好的产品经理，因为你不但对用户把握得很好，也会对软件工艺把握得很好。你不但会开出外在的功能性需求，也同样会开出内在的让软件系统更完善的非功能性需求。你不是在迁就用户，而是引导用户。你不会无限制地加功能，而是把握产品灵魂控制并简化功能。你会为自己要做的和不做东西的感到同样的自豪。”你要告诉你的产品经理：“做一个半成品不如做好半年产品”（更多这样的观点请参看《<a title="《Rework》摘录及感想" href="https://coolshell.cn/articles/9156.html" target="_blank">Rework摘录和感想</a>》）</p>
<p style="padding-left: 30px;">3）做事情是要讲效率的。Amazon里喜欢使用一种叫T-Shirt Size Estimation的评估方法来优先做投入小产出大的“Happy Case”。关于什么是效率，什么是T-Shirt Size Estimation，你可以看看《<a title="加班与效率" href="https://coolshell.cn/articles/10217.html" target="_blank">加班与效率</a>》一文 。</p>
<p style="padding-left: 30px;">4）需求总是会变化的，不要抱怨需求变化太快。你应该抱怨的是为什么我们没有把握好方向？老变？这个事就像踢足球一样，你要去的地方是球将要去的地方，而不是球现在的地方。你要知道球要去哪里，你就知道球之前是怎么动的，找到了运动轨迹后，你才知道球要去像何方。如果你都不知道球要去向何方，那你就是一只无头苍蝇一样，东一下西一下。</p>
<p><strong>当你忙得跟牲口一样，你应该停下来，问一下自己，自己成为牲口的原因，是不是就是因为自己做事时候像就牲口一样思考？</strong></p>
<h4>其它</h4>
<p>最后，我在给阿里今年新入职的毕业生的“技塑人生”的分享中，我给他们布置了5、6个Homework，分享几个给大家：</p>
<p style="padding-left: 30px;">1）重构或写一个模块，把他做成真正的Elegant级别。</p>
<p style="padding-left: 30px;">2）与大家分享一篇或几篇技术文章 ，并收获10-30个赞。</p>
<p style="padding-left: 30px;">3）降低现有至少20%的重复工作或维护工作</p>
<p style="padding-left: 30px;">4）拒绝或简化一个需求（需要项目中所有的Stakeholders都同意）</p>
<p>部署这些作业的原因，是我希望新入行的同学们对自己的工作坚持高的标准，我知道你们会因为骨感的现实而妥协，但是我希望你们就算在现实中妥协了也要在内心中坚持尽可能高的标准，不要习惯成自然，最后被社会这个大染缸给潜移默化了。因为你至少要对自己负责。<strong>对自己负责就是，用脚投票，如果妥协得受不了了就离开吧</strong>。</p>
<p>芝兰生于空谷，不以无人而不芳！君子修身养道，不以穷困而改志！</p>
<p>谢谢听我唠叨。</p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/4875.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/06/20110620115951113-150x150.gif" alt="一个空格引发的惨剧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4875.html" class="wp_rp_title">一个空格引发的惨剧</a></li><li ><a href="https://coolshell.cn/articles/22298.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/10/communication-150x150.png" alt="聊聊团队协同和协同工具" width="150" height="150" /></a><a href="https://coolshell.cn/articles/22298.html" class="wp_rp_title">聊聊团队协同和协同工具</a></li><li ><a href="https://coolshell.cn/articles/22173.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/02/http_method-150x150.png" alt="“一把梭：REST API 全用 POST”" width="150" height="150" /></a><a href="https://coolshell.cn/articles/22173.html" class="wp_rp_title">“一把梭：REST API 全用 POST”</a></li><li ><a href="https://coolshell.cn/articles/22157.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/02/monitoring-150x150.jpeg" alt="谈谈公司对员工的监控" width="150" height="150" /></a><a href="https://coolshell.cn/articles/22157.html" class="wp_rp_title">谈谈公司对员工的监控</a></li><li ><a href="https://coolshell.cn/articles/21589.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2021/07/knowledge_sharing-300x169-1-150x150.jpeg" alt="如何做一个有质量的技术分享" width="150" height="150" /></a><a href="https://coolshell.cn/articles/21589.html" class="wp_rp_title">如何做一个有质量的技术分享</a></li><li ><a href="https://coolshell.cn/articles/20977.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2020/08/programmer.01-e1596792460687-150x150.png" alt="程序员如何把控自己的职业" width="150" height="150" /></a><a href="https://coolshell.cn/articles/20977.html" class="wp_rp_title">程序员如何把控自己的职业</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/11432.html">从Code Review 谈如何做技术</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/11432.html/feed</wfw:commentRss>
			<slash:comments>218</slash:comments>
		
		
			</item>
		<item>
		<title>一个空格引发的惨剧</title>
		<link>https://coolshell.cn/articles/4875.html</link>
					<comments>https://coolshell.cn/articles/4875.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Mon, 20 Jun 2011 00:26:34 +0000</pubDate>
				<category><![CDATA[Unix/Linux]]></category>
		<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[轶事趣闻]]></category>
		<category><![CDATA[bumblebee]]></category>
		<category><![CDATA[Code Review]]></category>
		<category><![CDATA[Coding]]></category>
		<category><![CDATA[Programmer]]></category>
		<category><![CDATA[程序员]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=4875</guid>

					<description><![CDATA[<p>你是否相信如果你的程序里没有检查一个变量会导致怎么系统瘫痪？无论你相不相信，这是我一个亲身经历过的案例，你可以在本站的程序员那些悲催的事儿中找到很多这样的事。这...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/4875.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/4875.html">一个空格引发的惨剧</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>你是否相信如果你的程序里没有检查一个变量会导致怎么系统瘫痪？无论你相不相信，这是我一个亲身经历过的案例，你可以在本站的<a title="程序员那些悲催的事儿" href="https://coolshell.cn/articles/3980.html" target="_blank">程序员那些悲催的事儿</a>中找到很多这样的事。这样的事昨天在发生，今天同样在发生。<a title="Unix传奇(上篇)" href="https://coolshell.cn/articles/2322.html" target="_blank">Unix40多年</a>了，在这40年里，程序员发生过各种各样的的惨剧，但是大多数的事情一而再再而三的重演。</p>
<p>今天的你，可能在开发者各种各样NB的系统，你会相信你的一个空格也能导致系统瘫痪吗？也许你可能很难相信这个事。不过，再下面这个事将告诉你这个血淋淋的事实 —— 一个空格产生的bug可以让你的系统瘫痪。</p>
<p><a href="https://github.com/MrMEEE/bumblebee">bumblebee</a>是一个开源项目，这个名字也就是变形金刚里的大黄蜂，这个项目是这样介绍自己的——</p>
<blockquote><p>bumblebee is Optimus support for Linux, with real offloading, and not switchable graphics.. More important.. it works on Optimus Laptops without a graphical multiplexer..</p></blockquote>
<p>Optimus 是NVIDIA的“优驰”技术，其可以将您的笔记本电脑PC提升到绝佳状态，提供出色的图形性能，并在需要时延长电池续航时间。这个项目是把这个技术移到Linux上来。</p>
<p>这个项目本来不出名，不过，程序在其安装脚本install.sh里的一个bug让这个项目一下子成了全世界最瞩目的项目，这个bug的fix如下：</p>
<pre data-enlighter-language="diff" class="EnlighterJSRAW">@@ -348,7 +348,7 @@ case &quot;$DISTRO&quot; in
-  rm -rf /usr /lib/nvidia-current/xorg/xorg
+  rm -rf /usr/lib/nvidia-current/xorg/xorg</pre>
<p>看明白了吗？<strong>空格</strong>。这个空格会导致什么样的问题呢？呵呵。你有没有感到菊花一紧？这个bug绝对的霸气外露！真是验证了<a title="如何写出无法维护的代码" href="https://coolshell.cn/articles/4758.html" target="_blank">“如何写出无法维护代码</a>”的那句话——“<strong>测试你的程序是一种懦夫的行为</strong>”。</p>
<p>不过，最精彩还不是这个bug，而是全世界程序员的对这个bug 的 code review comments，真的相当的欢乐。请强势围望！</p>
<p><span id="more-4875"></span></p>
<p style="text-align: center;"><a href="https://github.com/MrMEEE/bumblebee/commit/a047be85247755cdbe0acce6#diff-1">https://github.com/MrMEEE/bumblebee/commit/a047be85247755cdbe0acce6#diff-1</a></p>
<p style="text-align: left;">重点是其中的很多图片——下面的图片众多。</p>
<p style="text-align: center;"><a href="http://pic003.cnblogs.com/2011/34358/201106/20110620115950761.gif"><img decoding="async" loading="lazy" title="clip_image001" src="http://pic003.cnblogs.com/2011/34358/201106/20110620115951113.gif" alt="clip_image001" width="500" height="275" /></a></p>
<p style="text-align: center;"><img decoding="async" src="http://pic003.cnblogs.com/2011/1/201106/2011062012551463.jpg" alt="" /></p>
<p style="text-align: center;"><a href="http://pic003.cnblogs.com/2011/34358/201106/20110620115950761.gif"><br />
</a></p>
<p style="text-align: center;"><img decoding="async" src="http://pic003.cnblogs.com/2011/1/201106/2011062012574297.jpg" alt="" /></p>
<p style="text-align: center;"><a href="http://pic003.cnblogs.com/2011/34358/201106/20110620115951580.jpg"><img decoding="async" loading="lazy" title="clip_image002" src="http://pic003.cnblogs.com/2011/34358/201106/20110620115951524.jpg" alt="clip_image002" width="292" height="302" border="0" /></a></p>
<p style="text-align: center;"><img decoding="async" src="http://pic003.cnblogs.com/2011/1/201106/2011062012590122.jpg" alt="" /></p>
<p style="text-align: center;"><img decoding="async" src="http://pic003.cnblogs.com/2011/1/201106/2011062013022333.jpg" alt="" /></p>
<p style="text-align: center;"><img decoding="async" src="http://pic003.cnblogs.com/2011/1/201106/2011062013033063.jpg" alt="" /></p>
<p style="text-align: center;"><img decoding="async" src="http://pic003.cnblogs.com/2011/1/201106/2011062013042755.jpg" alt="" /></p>
<p style="text-align: center;"><a href="http://pic003.cnblogs.com/2011/34358/201106/20110620115954514.jpg"><img decoding="async" loading="lazy" title="clip_image007" src="http://pic003.cnblogs.com/2011/34358/201106/2011062011595582.jpg" alt="clip_image007" width="406" height="455" border="0" /></a></p>
<p style="text-align: center;"><a href="http://pic003.cnblogs.com/2011/34358/201106/20110620115958341.jpg"><img decoding="async" loading="lazy" title="clip_image010" src="http://pic003.cnblogs.com/2011/34358/201106/20110620115958644.jpg" alt="clip_image010" width="401" height="299" border="0" /></a></p>
<p style="text-align: center;"><a href="http://pic003.cnblogs.com/2011/34358/201106/20110620115958163.jpg"><img decoding="async" loading="lazy" title="clip_image011" src="http://pic003.cnblogs.com/2011/34358/201106/20110620115959784.jpg" alt="clip_image011" width="408" height="404" border="0" /></a></p>
<p style="text-align: center;"><a href="http://pic003.cnblogs.com/2011/34358/201106/20110620115959641.jpg"><img decoding="async" loading="lazy" title="clip_image012" src="http://pic003.cnblogs.com/2011/34358/201106/20110620120001976.jpg" alt="clip_image012" width="400" height="401" border="0" /></a></p>
<p style="text-align: center;"><img decoding="async" src="http://pic003.cnblogs.com/2011/1/201106/2011062013060775.jpg" alt="" /></p>
<p style="text-align: center;"><a href="http://pic003.cnblogs.com/2011/34358/201106/20110620120001777.gif"><img decoding="async" loading="lazy" title="clip_image014" src="http://pic003.cnblogs.com/2011/34358/201106/20110620120001634.gif" alt="clip_image014" width="400" height="463" border="0" /></a></p>
<p style="text-align: center;"><img decoding="async" src="http://pic003.cnblogs.com/2011/1/201106/2011062013073049.jpg" alt="" /></p>
<p style="text-align: center;"><img decoding="async" loading="lazy" src="http://pic003.cnblogs.com/2011/34358/201106/20110620120002955.gif" alt="" width="480" height="360" border="0" /></p>
<p style="text-align: center;"><img decoding="async" src="http://pic003.cnblogs.com/2011/1/201106/2011062013083437.jpg" alt="" /></p>
<p style="text-align: center;"><img decoding="async" src="http://pic003.cnblogs.com/2011/1/201106/2011062013090259.jpg" alt="" /></p>
<p style="text-align: center;"><a href="http://pic003.cnblogs.com/2011/34358/201106/20110620120002899.jpg"><img decoding="async" loading="lazy" title="clip_image016" src="http://pic003.cnblogs.com/2011/34358/201106/20110620120002202.jpg" alt="clip_image016" width="512" height="384" border="0" /></a></p>
<p style="text-align: center;"><img decoding="async" src="http://pic003.cnblogs.com/2011/1/201106/2011062013110568.jpg" alt="" /></p>
<p style="text-align: center;"><img decoding="async" src="http://pic003.cnblogs.com/2011/1/201106/2011062013121496.jpg" alt="" /></p>
<p style="text-align: center;"><a href="http://pic003.cnblogs.com/2011/34358/201106/20110620120002666.jpg"><img decoding="async" loading="lazy" title="clip_image019" src="http://pic003.cnblogs.com/2011/34358/201106/20110620120002718.jpg" alt="clip_image019" width="397" height="804" border="0" /></a></p>
<p style="text-align: center;"><a href="http://pic003.cnblogs.com/2011/34358/201106/20110620120003129.jpg"><img decoding="async" loading="lazy" title="clip_image020" src="http://pic003.cnblogs.com/2011/34358/201106/20110620120003540.jpg" alt="clip_image020" width="594" height="488" border="0" /></a></p>
<p style="text-align: center;"><a href="http://pic003.cnblogs.com/2011/34358/201106/2011062012000453.jpg"><img decoding="async" loading="lazy" title="clip_image021" src="http://pic003.cnblogs.com/2011/34358/201106/20110620120004356.jpg" alt="clip_image021" width="400" height="290" border="0" /></a></p>
<p><img decoding="async" class="aligncenter" src="http://pic003.cnblogs.com/2011/1/201106/2011062013135533.jpg" alt="" /></p>
<p style="text-align: left;">(全文完)</p>
<p><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/18024.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg" alt="API设计原则 &#8211; Qt官网的设计实践总结" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18024.html" class="wp_rp_title">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href="https://coolshell.cn/articles/11432.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/04/code_review-150x150.jpg" alt="从Code Review 谈如何做技术" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11432.html" class="wp_rp_title">从Code Review 谈如何做技术</a></li><li ><a href="https://coolshell.cn/articles/5444.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg" alt="千万不要把 bool 设计成函数参数" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5444.html" class="wp_rp_title">千万不要把 bool 设计成函数参数</a></li><li ><a href="https://coolshell.cn/articles/5201.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/08/538efefbjw1dt8f6ua5rpg-150x150.gif" alt="重构代码的7个阶段" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5201.html" class="wp_rp_title">重构代码的7个阶段</a></li><li ><a href="https://coolshell.cn/articles/4758.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/30.jpg" alt="如何写出无法维护的代码" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4758.html" class="wp_rp_title">如何写出无法维护的代码</a></li><li ><a href="https://coolshell.cn/articles/3005.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/27.jpg" alt="代码重构的一个示例" width="150" height="150" /></a><a href="https://coolshell.cn/articles/3005.html" class="wp_rp_title">代码重构的一个示例</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/4875.html">一个空格引发的惨剧</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/4875.html/feed</wfw:commentRss>
			<slash:comments>106</slash:comments>
		
		
			</item>
		<item>
		<title>橡皮鸭程序调试法</title>
		<link>https://coolshell.cn/articles/1719.html</link>
					<comments>https://coolshell.cn/articles/1719.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Tue, 10 Nov 2009 10:00:06 +0000</pubDate>
				<category><![CDATA[流程方法]]></category>
		<category><![CDATA[Code Review]]></category>
		<category><![CDATA[Debug]]></category>
		<category><![CDATA[Rubber Duck]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=1719</guid>

					<description><![CDATA[<p>下面，让我来为你介绍一个程序调试大法——“橡皮鸭程序调试法”，这个方法在调试界是很出众的，实施起来相当方便和简易，几乎可以随时随地地实验，几乎不需要借助任何的软...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/1719.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/1719.html">橡皮鸭程序调试法</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><img decoding="async" loading="lazy" class="size-thumbnail wp-image-1721 alignright" title="Rubber Duck Debugging" src="https://coolshell.cn/wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg" alt="Rubber Duck Debugging" width="150" height="150" srcset="https://coolshell.cn/wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg 150w, https://coolshell.cn/wp-content/uploads/2009/11/Rubber-Duck-200x200.jpg 200w" sizes="(max-width: 150px) 100vw, 150px" />下面，让我来为你介绍一个程序调试大法——“橡皮鸭程序调试法”，这个方法在调试界是很出众的，实施起来相当方便和简易，几乎可以随时随地地实验，几乎不需要借助任何的软件和硬件的支持，你甚至可以把你的程序打印出来，在纸面上进行调试。</p>
<p>那么，为什么这个方法要叫做橡皮鸭呢？因为橡皮鸭子是西方人在泡澡时最喜欢玩的一个小玩具，所以，这个东西应该家家户户都必备的。因为，这个方法由西方人发明，所以，就被取名为“橡皮鸭”了。</p>
<p>好了，话不多说，下面是整个调试方法的流程。</p>
<ol>
<li>找一个橡皮鸭子。你可以去借，去偷，去抢，去买，自己制作……反正你要搞到一个橡皮鸭子。</li>
<li>把这个橡皮鸭子放在你跟前。标准做法是放在你的桌子上，电脑显示器边，或是键盘边，反正是你的跟前，面朝你。</li>
<li>然后，打开你的源代码。不管是电脑里的还是打印出来的。</li>
<li>对着那只橡皮鸭子，把你写下的所有代码，一行一行地，精心地，向这只橡皮鸭子解释清楚。记住，这是解释，你需要解释出你的想法，思路，观点。不然，那只能算是表述，而不是解释。</li>
<li>当你在向这只始终保持沉默的橡皮鸭子解释的过程中，你会发现你的想法，观点，或思路和实际的代码相偏离了，于是你也就找到了代码中的bug。</li>
<li>找到了BUG，一定要记得感谢一下那个橡皮鸭子哦。</li>
</ol>
<p>什么？你觉得这个方法太“愚蠢”，太“弱智”了？是的，看上去，会这样做的人脑子好像是有点毛病。不过，我要告诉你的是，这个方法的确有效。<strong>因为，这就是“Code Review”的雏形</strong>！下面让我来给你解释一下。</p>
<p><span id="more-1719"></span></p>
<blockquote><p>Once a problem is described in sufficient detail, its solution is obvious.</p></blockquote>
<p>上面这句话的意思是</p>
<blockquote><p>一旦一个问题被充分地描述了他的细节，那么解决方法也是显而易见的。</p></blockquote>
<p>我相信在座的各位都有过这样的经历，当你死活都找不到问题的原因的时候，当你寻求他人的帮助时，对别人解释整个你的想法和意图或是问题背景的时候，你自己都没有解释完，就已经找到问题的原因了。这样的经历，相信大家一定有过。这就是这个方法的意义所在。</p>
<p>所以，“橡皮鸭”只是一个形式，其主要目的是要你把自己写的代码做“自查”，也就是自己解释给自己听。当然，为了不让你像个“精神分裂”的程序员，引入“橡皮鸭”是很有必要的（虽然这样还是有点精神病，但比起精神分裂来说算是好的了，嘻嘻）。所以，真实的本质是Code Review。关于代码评审，大家可以看一下我的这篇文章《<a rel="bookmark" href="https://coolshell.cn/articles/1302.html">Code Review中的几个提示</a>》，你会明白其中更多的东西的。</p>
<p>最后，我想和大家说一下道具“橡皮鸭”。是的，在我们的身边，你不一定能找得“橡皮鸭”，但你可以找到你你的同事，你的朋友，来做这个“橡皮鸭”，当然，他们并不一定有“橡皮鸭”好使，因为你的那些同事或朋友一定会在你解释的时候，随意地发表意见和看法，相当的令人annoying。《<a rel="bookmark" href="https://coolshell.cn/articles/1302.html">Code Review中的几个提示</a>》和《<a rel="bookmark" href="https://coolshell.cn/articles/16.html">结对编程的利与弊</a>》也谈到了一些，供你借鉴。</p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/22320.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2022/12/eBPF-150x150.jpeg" alt="eBPF 介绍" width="150" height="150" /></a><a href="https://coolshell.cn/articles/22320.html" class="wp_rp_title">eBPF 介绍</a></li><li ><a href="https://coolshell.cn/articles/17757.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411-150x150.jpg" alt="如何重构“箭头型”代码" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17757.html" class="wp_rp_title">如何重构“箭头型”代码</a></li><li ><a href="https://coolshell.cn/articles/11432.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/04/code_review-150x150.jpg" alt="从Code Review 谈如何做技术" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11432.html" class="wp_rp_title">从Code Review 谈如何做技术</a></li><li ><a href="https://coolshell.cn/articles/4875.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/06/20110620115951113-150x150.gif" alt="一个空格引发的惨剧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4875.html" class="wp_rp_title">一个空格引发的惨剧</a></li><li ><a href="https://coolshell.cn/articles/1525.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/24.jpg" alt="GDB 7.0 发布" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1525.html" class="wp_rp_title">GDB 7.0 发布</a></li><li ><a href="https://coolshell.cn/articles/1502.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/26.jpg" alt="高科技：GDB回溯调试" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1502.html" class="wp_rp_title">高科技：GDB回溯调试</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/1719.html">橡皮鸭程序调试法</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/1719.html/feed</wfw:commentRss>
			<slash:comments>26</slash:comments>
		
		
			</item>
		<item>
		<title>Code Review中的几个提示</title>
		<link>https://coolshell.cn/articles/1302.html</link>
					<comments>https://coolshell.cn/articles/1302.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Thu, 20 Aug 2009 15:49:49 +0000</pubDate>
				<category><![CDATA[技术读物]]></category>
		<category><![CDATA[流程方法]]></category>
		<category><![CDATA[Code Review]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=1302</guid>

					<description><![CDATA[<p>Code Review应该是软件工程最最有价值的一个活动，之前，本站发表过《简单实用的Code Review工具》，那些工具主要是用来帮助更有效地进行这个活动，...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/1302.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/1302.html">Code Review中的几个提示</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><img decoding="async" loading="lazy" class="alignright size-full wp-image-1303" title="Code Reivew" alt="Code Reivew" src="https://coolshell.cn/wp-content/uploads/2009/08/review.jpg" width="124" height="88" />Code Review应该是软件工程最最有价值的一个活动，之前，本站发表过《<a href="https://coolshell.cn/articles/1218.html" rel="bookmark">简单实用的Code Review工具</a>》，那些工具主要是用来帮助更有效地进行这个活动，这里的这篇文章，我们主要想和大家分享一下Code Review代码审查的一些心得。</p>
<p>首先，我们先来看看Code Reivew的用处：</p>
<ol>
<li>Code reviews 中，可以通过大家的建议增进代码的质量。</li>
<li>Code reviews  是一个传递知识的手段，可以让其它并不熟悉代码的人知道作者的意图和想法，从而可以在以后轻松维护代码。</li>
<li>Code reviews 也鼓励程序员们相互学习对方的长处和优点。</li>
<li>Code reviews 也可以被用来确认自己的设计和实现是一个清楚和简单的。</li>
</ol>
<p>你也许注意到了在上面的Code Reivew中的诸多用处中，我们没有提到可以帮助找到程序的bug和保证代码风格和编码标准。这是因为我们认为：</p>
<p><span id="more-1302"></span></p>
<ol>
<li><strong>Code reviews <span style="color: #993300;">不应该</span>承担发现代码错误的职责</strong>。Code Review主要是审核代码的质量，如可读性，可维护性，以及程序的逻辑和对需求和设计的实现。代码中的bug和错误应该由单元测试，功能测试，性能测试，回归测试来保证的（其中主要是单元测试，因为那是最接近Bug，也是Bug没有扩散的地方）</li>
<li><strong>Code reviews <span style="color: #993300;">不应该</span>成为保证代码风格和编码标准的手段</strong>。编码风格和代码规范都属于死的东西，每个程序员在把自己的代码提交团队Review的时候，代码就应该是符合规范的，这是默认值，属于每个人自己的事情，不应该交由团队来完成，否则只会浪费大家本来就不够的时间。我个人认为“meeting”是奢侈的，因为那需要大家在同一时刻都挤出时间，所以应该用在最需要的地方。代码规范比起程序的逻辑和对需求设计的实现来说，太不值得让大家都来了。</li>
</ol>
<p>10年前，上面这两件事会是理所当然的（10年前的中国的软件开发还没有Code Reivew呢），今天，在中国的很多公司上面这两件事依然被认为是Code Reivew最重要的事，所以，我能够看到很多开发Team抱怨Code Review就是一个形式，费时费力不说，发现的问题还不如测试，而评审者们除了在代码风格上有些见术，别的也就没什么用了，长而久之，大家都会开始厌烦这个事了。</p>
<p>所以，在今天，请不要把上面的那两件事分散了Code Review的注意力，取而代之的是，对于Bug，程序的作者要在Review前提交自己的单元测试报告（如：XUnit的测试结果），对于代码规范，这是程序作者自己需要保证的，而且，有一些工具是可以帮你来检查代码规范的。</p>
<p><strong>当然，上述这些言论并不是说，你不能在Code Review中报告一个程序的bug或是一个代码规范的问题。我只是说，那并不是Code Review的意图。</strong></p>
<p>下面是我们认为的几个小提示可以让你更好进行Code Review这项最有价值的活动。</p>
<h4>1.- 经常进行Code Review</h4>
<p>以前经历过几个相当痛苦的Code Review，那几次Code Review都是在程序完成的时候进行的，当你面对那近万行的代码，以前N多掺和在一起的功能，你会发现，整个Code Review变得非常地艰难，用不了一会儿，你就会发现大家都在拼命地打着哈欠，但还是要坚持，有时候，这样的Review会持续3个小时以上，相当的夸张。而且，会议上会出现相当多的问题和争论，因为，这就好像，人家都把整个房子盖好了，大家Review时这挑一点那挑一点，有时候触动地基或是承重墙体，需要大动手术，让人返工，这当然会让盖房的人一下就跳起来极力地维护自己的代码，最后还伤了团队成员的感情。</p>
<p>所以，千万不要等大厦都盖好了再去Reivew，而且当有了地基，有了框架，有了房顶，有了门窗，有了装修，的各个时候循序渐进地进行Review，这样反而会更有效率，也更有帮助。</p>
<p>下面是一些观点，千万要铭记：</p>
<ul>
<li><strong>要Review的代码越多，那么要重构，重写的代码就会越多。而越不被程序作者接受的建议也会越多，唾沫口水战也会越多。<br />
</strong></li>
<li><strong>程序员代码写得时候越长，程序员就会在代码中加入越来越多的个人的东西。</strong> 程序员最大的问题就是“自负”，无论什么时候，什么情况下，有太多的机会会让这种“自负”澎涨开来，并开始影响团队影响整个项目，以至于听不见别人的建议，从而让Code Review变成了口水战。</li>
<li><strong>越接近软件发布的最终期限，代码也就不能改得太多。</strong></li>
</ul>
<p>我个人的习惯，并且也是对团队成员的要求是——先Review设计实现思路，然后Review设计模式，接着Review成形的骨干代码，最后Review完成的代码，如果程序复杂的话，需要拆成几个单元或模块分别Review。当然，最佳的practice是，每次Review的代码应该在1000行以内，时间不能超过一部电影的时间——1.5小时（因为据说那个一个正常人的膀胱可以容纳尿液的最长限度）</p>
<p>当然，在敏捷开发中，他们不需要Code Reivew，其实，敏捷开发中使用更为极端的“结对编程”（Pair-Programming）的方法 —— 一种时时刻刻都在进行Code Review的方法，个人感觉在实际过程中，这种方法有点过了。另外，大家可以看看本站的另一篇文章《<a href="https://coolshell.cn/articles/16.html" rel="bookmark">结对编程的利与弊</a>》来了解一下这种方法的问题。</p>
<h4>2.- Code Review不要太正式，而且要短</h4>
<p>忘了那个代码评审的Checklist吧，走到你的同事座位跟前，像请师父一样请他坐到你的电脑面前，然后，花5分钟给他讲讲你的代码，给他另外一个5分钟让他给你的代码提提意见，这比什么都好。而如果你用了一个Checklist，让这个事情表现得很正式的话，下面两件事中必有一件事会发生：</p>
<ol>
<li>只有在Checklist上存在的东西才会被Review。</li>
<li>Code Reviews 变成了一种礼节性的东西，你的同事会装做很关心你的代码，但其实他心里想着尽快地离开你。</li>
</ol>
<p>只有不正式的Code Review才会让你和评审者放轻松，人只有放松了，才会表现得很真实，很真诚。记住Review只不过是一种形式，而只有在相互信任中通过相互的讨论得到了有意义和有建设性的建议和意见，那才是最实在的。不然，作者和评审者的关系就会变成小偷和警察的关系。</p>
<h4>3.- 尽可能的让不同的人Reivew你的代码</h4>
<p>这是一个好主意，如果可能的话，不要总是只找一个人来Review你的代码，不同的人有不同的思考方式，有不同的见解，所以，不同的人可以全面的从各个方面评论你的代码，有的从实现的角度，有的从需求的角度，有的从用户使用的角度，有的从算法的角度，有的从性能效率的角度，有的从易读的角度，有的从扩展性的角度……，啊，好多啊，还让不让人活了。不管怎么说，多找一些不同的人会对你很有好处。当然，不要太多了，人多嘴杂反而适得其反，基本上来说，不要超过3个人，这是因为，这是一个可以围在一起讨论的最大人员尺寸。</p>
<p>下面是几个优点：</p>
<ol>
<li>从不同的方向评审代码总是好的。</li>
<li>会有更多的人帮你在日后维护你的代码。</li>
<li>这也是一个增加团队凝聚力的方法。</li>
</ol>
<h4>4.- 保持积极的正面的态度</h4>
<p>再说一次，程序最大的问题就是“自负”，尤其当我们Reivew别人的代码的时候，我已经见过无数的场面，程序员在Code Review的时候，开始抨击别人的代码，质疑别人的能力。太可笑了，我分析了一下，这类的程序员其实并没有什么本事，因为他们指责对方的目的是想告诉大家自己有多么的牛，靠这种手段来表现自己的程序员，其实是就是传说中所说的“半瓶水”。</p>
<p>所以，无论是代码作者，还是评审者，都需要一种积极向上的正面的态度，作者需要能够虚心接受别人的建议，因为别人的建议是为了让你做得更好；评审者也需要以一种积极的正面的态度向作者提意见，因为那是和你在一个战壕里的战友。记住，你不是一段代码，你是一个人！</p>
<h4>5.- 学会享受Code Reivew</h4>
<p>这可能是最重要的一个提示了，如果你到了一个人人都喜欢Code Reivew的团阿，那么，你会进入到一个生机勃勃的地方，在那里，每个人都能写出质量非常好的代码，在那里，你不需要经理的管理，团队会自适应一切变化，他们相互学习，相互帮助，不仅仅是写出好的代码，而且团队和其中的每个人都会自动进化，最关键的是，这个是一个团队。</p>
<p>（全文完）<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/17757.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411-150x150.jpg" alt="如何重构“箭头型”代码" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17757.html" class="wp_rp_title">如何重构“箭头型”代码</a></li><li ><a href="https://coolshell.cn/articles/11432.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/04/code_review-150x150.jpg" alt="从Code Review 谈如何做技术" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11432.html" class="wp_rp_title">从Code Review 谈如何做技术</a></li><li ><a href="https://coolshell.cn/articles/4875.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/06/20110620115951113-150x150.gif" alt="一个空格引发的惨剧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4875.html" class="wp_rp_title">一个空格引发的惨剧</a></li><li ><a href="https://coolshell.cn/articles/1719.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/11/Rubber-Duck-150x150.jpg" alt="橡皮鸭程序调试法" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1719.html" class="wp_rp_title">橡皮鸭程序调试法</a></li><li ><a href="https://coolshell.cn/articles/1218.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2009/08/viewtopicdetail-150x150.png" alt="简单实用的Code Review工具" width="150" height="150" /></a><a href="https://coolshell.cn/articles/1218.html" class="wp_rp_title">简单实用的Code Review工具</a></li><li ><a href="https://coolshell.cn/articles/8790.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/12/choice-150x150.jpg" alt="程序算法与人生选择" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8790.html" class="wp_rp_title">程序算法与人生选择</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/1302.html">Code Review中的几个提示</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/1302.html/feed</wfw:commentRss>
			<slash:comments>39</slash:comments>
		
		
			</item>
		<item>
		<title>简单实用的Code Review工具</title>
		<link>https://coolshell.cn/articles/1218.html</link>
					<comments>https://coolshell.cn/articles/1218.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Tue, 04 Aug 2009 09:09:13 +0000</pubDate>
				<category><![CDATA[杂项资源]]></category>
		<category><![CDATA[编程工具]]></category>
		<category><![CDATA[Code Review]]></category>
		<category><![CDATA[Codestriker]]></category>
		<category><![CDATA[Coding]]></category>
		<category><![CDATA[Groogle]]></category>
		<category><![CDATA[JCR]]></category>
		<category><![CDATA[Jupiter]]></category>
		<category><![CDATA[Review board]]></category>
		<category><![CDATA[Rietveld]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=1218</guid>

					<description><![CDATA[<p>Code Review中文应该译作“代码审查”或是“代码评审”，这是一个流程，当开发人员写好代码后，需要让别人来review一下他的代码，这是一种有效发现BUG...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/1218.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/1218.html">简单实用的Code Review工具</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script><img decoding="async" loading="lazy" class="alignright" title="Code Review" src="http://www.review-board.org/media/rbsite/images/logo.png?1238930581" alt="" width="130" height="123" />Code Review中文应该译作“代码审查”或是“代码评审”，这是一个流程，当开发人员写好代码后，需要让别人来review一下他的代码，这是一种有效发现BUG的方法。由此，我们可以审查代码的风格、逻辑、思路……，找出问题，以及改进代码。因为这是代码刚刚出炉的时候，所以，这也是代码重构，代码调整，代码修改的最佳时候。所以，Code Review是编码实现中最最重要的一个环节。</p>
<p>长时间以来，Code Review需要有一些有效的工具来支持，这样我们就可以更容易，更有效率地来进行代码审查工作。下面是5个开源的代码审查工具，他们可以帮助你更容易地进行这项活动。</p>
<p><strong>1. <a href="http://www.review-board.org/" target="_blank">Review board</a>:</strong><br />
<a href="http://www.review-board.org/" target="_blank">Review board</a> 是一个 基于web 的工具，是由 <a href="http://www.djangoproject.com/" target="_blank">django</a> 和<a href="http://www.python.org/" target="_blank">python</a>设计的。 <a href="http://www.review-board.org/" target="_blank">Review board</a> 可以帮助我们追踪待决代码的改动，并可以让Code-Review更为容易和简练。尽管<a href="http://www.review-board.org/" target="_blank">Review board</a> 最初被设计在<a href="http://www.vmware.com/" target="_blank">VMware</a>项目中使用，但现在其足够地通用。当前，其支持这些代码版本管理软件： <a href="http://subversion.tigris.org/" target="_blank">SVN</a>, CVS, <a href="http://www.perforce.com/" target="_blank">Perforce</a>, <a href="http://git-scm.com/" target="_blank">Git</a>, <a href="http://bazaar-vcs.org/" target="_blank">Bazaar</a>, 和<a href="http://www.selenic.com/mercurial/wiki/" target="_blank">Mercurial</a>.</p>
<p><span id="more-1218"></span></p>
<p>Yahoo 是<a href="http://www.review-board.org/" target="_blank">review-board</a>的其中一个用户。</p>
<p style="padding-left: 30px;">“<a href="http://www.review-board.org/" target="_blank">Review board</a> 已经改变了代码评审的方式，其可以强迫高质量的代码标准和风格，并可以成为程序员编程的指导者。每一次，当你访问search.yahoo.com 时，其代码都是使用 <a href="http://www.review-board.org/" target="_blank">Review board</a>工具Review过的。 We’re great fans of your work!” &#8211; Yahoo! Web Search</p>
<h3><a href="http://www.review-board.org/media/screenshots/2009/02/02/review-requests.png"><img decoding="async" class="aligncenter" src="http://www.review-board.org/media/screenshots/2009/02/02/review-requests_thumb.png" alt="Detailed review requests" /></a></h3>
<div><a href="http://www.review-board.org/media/screenshots/2009/02/02/diffviewer.png"><img decoding="async" class="aligncenter" src="http://www.review-board.org/media/screenshots/2009/02/02/diffviewer_thumb.png" alt="Powerful diff viewer" /></a></div>
<p><strong>2. <a href="http://codestriker.sourceforge.net/" target="_blank">Codestriker</a>:</strong><br />
<a href="http://codestriker.sourceforge.net/" target="_blank">Codestriker</a> 也是一个基于Web的应用，其主要使用 GCI-Perl 脚本支持在线的代码审查。<a href="http://codestriker.sourceforge.net/" target="_blank">Codestriker</a> 可以集成于CVS, <a href="http://subversion.tigris.org/" target="_blank">Subversion</a>, <a href="http://www-01.ibm.com/software/awdtools/clearcase/" target="_blank">ClearCase</a>, <a href="http://www.perforce.com/" target="_blank">Perforce</a> 和Visual SourceSafe。并有一些插件可以提供支持其它的源码管理工具。</p>
<p>David Sitsky 是 <a href="http://codestriker.sourceforge.net/" target="_blank">Codestriker</a> 的作者，并也是最活跃的开发人员之一。 Jason Remillard 是另一个活路的开发者，并给这个项目提供了最深远最有意义的贡献。大量的程序员贡献他们的代码给 <a href="http://codestriker.sourceforge.net/" target="_blank">Codestriker</a> 项目，导致了这个项目空前的繁荣。</p>
<p><img decoding="async" loading="lazy" class="aligncenter" src="http://codestriker.sourceforge.net/viewtopicdetail.png" alt="http://codestriker.sourceforge.net/viewtopicdetail.png" width="686" height="544" /></p>
<p><strong>3. <a href="http://groogle.sourceforge.net/" target="_blank">Groogle</a>:</strong><br />
<a href="http://groogle.sourceforge.net/" target="_blank">Groogle</a> 是一个基于WEB的代码评审工具。 <a href="http://groogle.sourceforge.net/" target="_blank">Groogle</a> 支持和 <a href="http://subversion.tigris.org/" target="_blank">Subversion</a> 集成。它主要提供如下的功能：</p>
<ul>
<li>各式各样语言的语法高亮。</li>
<li>支持整个版本树的比较。</li>
<li>支持当个文件不同版本的diff功能，并有一个图形的版本树。</li>
<li>邮件通知所有的Reivew的人当前的状态。</li>
<li>认证机制。</li>
</ul>
<p><img decoding="async" loading="lazy" class="aligncenter" src="http://sourceforge.net/dbimage.php?id=218190" alt="Screenshot" width="598" height="480" border="1" /></p>
<p><strong>4. <a href="http://code.google.com/p/rietveld/" target="_blank">Rietveld</a>:</strong><br />
<a href="http://code.google.com/p/rietveld/" target="_blank">Rietveld</a> 由Guido van Rossum 开发（他是Python的创造者，现在是Google的员工），这个工具是基于Mondrian 工具，作者一开始是为了Google 开发的，并且，它在很多方面和<a href="http://www.review-board.org/" target="_blank">Review board</a> 很像。它也是一个基于Web的应用，并在<a href="http://code.google.com/appengine/" target="_blank">Google App Engine</a> 上。它使用了目前最流行的Web开发框架 <a href="http://www.djangoproject.com/" target="_blank">django</a> 并支持 <a href="http://subversion.tigris.org/" target="_blank">Subversion</a> 。当前，任何一个使用 Google Code 的项目都可以使用 <a href="http://code.google.com/p/rietveld/" target="_blank">Rietveld</a> 并且使用 <a href="http://www.python.org/" target="_blank">python</a> <a href="http://subversion.tigris.org/" target="_blank">Subversion</a> 服务器。当然，它同样支持其它的Subversion服务器。</p>
<p><span style="position: relative;"><a href="javascript:dyn.onClickNextTbn()"><img decoding="async" id="imgb" class="aligncenter" style="width: 497px; height: 375px;" title="下一张" src="http://info-database.csdn.net/Upload/2008-11-13/Reviewboard.jpg" alt="" /></a></span></p>
<p><span style="position: relative;"> </span></p>
<p><strong>5.<a href="http://jcodereview.sourceforge.net/" target="_blank"> JCR</a></strong><br />
<a href="http://jcodereview.sourceforge.net/" target="_blank">JCR</a> 或者叫做 JCodeReview 也是一个基于WEB界面的最初设计给Reivew Java 语言的一个工具。当然，现在，它可以被用于其它的非Java的代码。</p>
<p><a href="http://jcodereview.sourceforge.net/" target="_blank">JCR</a> 主要想协助：</p>
<ul>
<li><strong>审查者</strong>。所有的代码更改都会被高亮，以及大多数语言的语法高亮。Code extracts 可以显示代码评审意见。如果你正在Review Java的代码，你可以点击代码中的类名来查看相关的类的声明。</li>
<li><strong>项目所有者</strong>。可以 轻松创建并配置需要Review的项目，并不需要集成任何的软件配置管理系统（SCM）。</li>
<li><strong>流程信仰者</strong>。 所有的评语都会被记录在数据库中，并且会有状态报告，以及各种各样的统计。</li>
<li><strong>架构师和开发者</strong>。 这个系统也可以让我们查看属于单个文件的评语，这样有利于我们重构代码。</li>
</ul>
<p><a href="http://jcodereview.sourceforge.net/" target="_blank">JCR</a> 主要面对的是大型的项目，或是非常正式的代码评审，从这方面看来，他并不像上面的那些工具。</p>
<p><img decoding="async" class="aligncenter" style="border: 1px solid black;" src="http://sourceforge.net/projects/jcodereview/screenshots/242251" alt="Screenshot" border="1" /></p>
<p><strong><a href="http://code.google.com/p/jupiter-eclipse-plugin/" target="_blank">Jupiter</a></strong>：最后我们要提一下<a href="http://code.google.com/p/jupiter-eclipse-plugin/" target="_blank">Jupiter</a>，这是另一个代码review的工具你可以去考虑使用的，它是一个Eclipse IDE 的插件。</p>
<p>文章：<a href="http://open-tube.com/easy-code-review-tools/" target="_blank">来源</a><!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/4875.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2011/06/20110620115951113-150x150.gif" alt="一个空格引发的惨剧" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4875.html" class="wp_rp_title">一个空格引发的惨剧</a></li><li ><a href="https://coolshell.cn/articles/18024.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg" alt="API设计原则 &#8211; Qt官网的设计实践总结" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18024.html" class="wp_rp_title">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href="https://coolshell.cn/articles/17757.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/04/IMG_7411-150x150.jpg" alt="如何重构“箭头型”代码" width="150" height="150" /></a><a href="https://coolshell.cn/articles/17757.html" class="wp_rp_title">如何重构“箭头型”代码</a></li><li ><a href="https://coolshell.cn/articles/11432.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2014/04/code_review-150x150.jpg" alt="从Code Review 谈如何做技术" width="150" height="150" /></a><a href="https://coolshell.cn/articles/11432.html" class="wp_rp_title">从Code Review 谈如何做技术</a></li><li ><a href="https://coolshell.cn/articles/8990.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/02/linus_pointer_to_pointer-150x150.jpg" alt="Linus：利用二级指针删除单向链表" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8990.html" class="wp_rp_title">Linus：利用二级指针删除单向链表</a></li><li ><a href="https://coolshell.cn/articles/8745.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg" alt="如此理解面向对象编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8745.html" class="wp_rp_title">如此理解面向对象编程</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/1218.html">简单实用的Code Review工具</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/1218.html/feed</wfw:commentRss>
			<slash:comments>21</slash:comments>
		
		
			</item>
	</channel>
</rss>
