我如何说服我的共同程序员不要做偏执“只是为了确保编程”?

时间:2009-07-30 10:29:58

标签: language-agnostic coding-style

我经常发现,当程序员或分配任务的人不能真正理解解决方案如何工作时,他们会随意添加内容直到它起作用。

示例:

重新绘制一个窗口,由于某种原因,它不会像程序员那样被绘制:

Invalidate();
Revalidate();
ProcessMessages(); 
Update();    
Repaint();
Repaint();
ProcessMessages(); 
Repaint();

过谨慎:

function test1(x: boolean) 
begin
  select case x
    true: // do something
    false: // do something else
    else
      raise Exception.Create("Invalid value.") // just to be sure
  end;
end;

function test2(x: Integer);
var
  y: Integer;
begin
  y = Abs(x);
  if y >= 0 then
  begin
    // do something
  end;
end;

虽然特别是过于谨慎的编码实践会导致大多数语言中的编译器警告,但我实际上已经在生产代码中看到了以上所有内容!

在大多数情况下,程序员和/或老板会对这种编码进行辩护。原因总是归结为这种反应:

  • 嗯,如果我们仔细检查会不会受伤?最好是安全而不是抱歉!
  • 这是防御性的编程,他们不是在大学里教这个吗?!

不幸的是,我没有充分的理由不这样做,虽然我仍然认为这是非常糟糕的风格,可以有不良后果。

我能否提出一些事实,这种风格最终会产生不良后果?

编辑:感谢您提出摆脱这种风格的好建议。但我仍然对原因感兴趣,我可以向同事们提出解释并可能说服他们,为什么这很糟糕,这样做符合他们的最佳利益是偏执狂。

17 个答案:

答案 0 :(得分:30)

让他们编写单元测试以涵盖每个案例。

答案 1 :(得分:23)

强制100%分支覆盖进行单元测试,否则构建失败。他将无法使test1抛出异常,或将test2中的条件评估为false。

答案 2 :(得分:12)

巧合编程 http://www.pragprog.com/the-pragmatic-programmer/extracts/coincidence

假设Fred有一个编程任务。 Fred在某些代码中键入,尝试它,它似乎工作。 Fred在更多代码中输入,尝试它,它似乎仍然有用。经过几周的编码,程序突然停止工作,经过几个小时的尝试修复后,他仍然不知道为什么。弗雷德可能会花费大量时间来追逐这段代码而无法修复它。无论他做什么,它似乎都无法正常工作。

弗雷德不知道为什么代码失败,因为他不知道为什么它首先起作用。考虑到Fred所做的“测试”有限,它似乎有效,但这只是巧合。在虚伪信心的鼓舞下,弗雷德被提前遗忘。现在,大多数聪明人都可能认识弗雷德这样的人,但我们知道的更好。我们不依赖巧合 - 我们呢?

答案 3 :(得分:9)

是的,这是一个问题。至少,这种类型的编程使代码难以理解和维护。如果有一个需要检查或捕获的真实案例,那么通常很难看出它是否真的经过了测试。即使像使用调试器单步执行代码这样的简单任务也会变得乏味。

我和一位编写这样的代码的初级开发人员争吵了很长时间。我无法用理智的说法说服他写入冗余检查或额外步骤与防御性编程不同。最终,我找到了一个解决方案:

我告诉开发人员,性能是他的部分代码的优先事项。事实证明,快速提高性能的最简单方法是删除额外的检查并重复重新初始化;-)。这个伎俩很有效,他很快就受到了“防守编码”习惯的训练!

答案 4 :(得分:6)

Repaint();
Repaint();
ProcessMessages(); 
Repaint();

这只是糟糕的编程。代码审查和培训必须在此处应用。

答案 5 :(得分:5)

你的观点是正确的,但据我所知,这些事情也是由于缺乏适当的技术知识造成的。我的意思是,我遇到的代码简直就是愚蠢的。怎么会有人写这样的东西 -

private bool IsValid(bool isValid)
{
    if(isValid == true) return true;
    else if(isValid == false) return false;
}

您提供的两个示例都是相同的。程序员可能不知道每个函数调用的作用(在第一种情况下)或switch情况的基本基础是什么(在第二种情况下)。不是吗?

答案 6 :(得分:4)

过度谨慎的原因包括:

  1. 这可能会使代码变得不必要地慢。
  2. 这将使代码更难理解,这可能意味着花费更多时间并且当其他人必须维护时会产生更多错误。
  3. 有证据表明他们真的不知道他们在做什么......或者他们懒得花时间思考和重写他们的代码。
  4. 向您的团队负责人/经理倾听,看看您是否可以让他们介绍代码评论和/或结对编程。

答案 7 :(得分:3)

  1. 让他们写单元测试
  2. 介绍代码评论(也许您可以讨论此类代码段并向他们展示更好的代码)
  3. 介绍DRY规则(不要重复你的意思)

答案 8 :(得分:3)

提供的第一个示例是Programming by coincidence的经典案例,因此您的弹药就是针对那个。

案例2和3在大多数情况下都是愚蠢的,除非它们是某些beta编程语言的测试用例,或者其中ABS和boolean的实现可能具有未定义的行为。

答案 9 :(得分:2)

这是一个非常烦人的问题,我曾多次面对过这个问题。试图通过向他们介绍各种原则或通过争论来说服某人只是证明令人沮丧和毫无结果。最后我采取了两种方法:

  • 当他们去的时候没有代码 午餐/家。
  • 改变机智并热情地 同意他们 - 经常这样 他们没有紧张,让他们思考 两次。

答案 10 :(得分:2)

  

y = Abs(x);如果y> = 0则

非常明智。 记得 - > MIN_INT == ABS(MIN_INT)

答案 11 :(得分:2)

只有一个解决方案:解雇他。

如果他无法正确编程,为什么让他继续浪费您的宝贵时间和成本?解雇他是唯一的方式让他重新思考他的习惯。在商业中,你无法表现出同情或同情。你不是交朋友。你在那里赚钱

如果您的开发人员不够好,只需雇用更好的开发人员。 那是经济应该如何运作。

答案 12 :(得分:1)

在他们自己的游戏中玩它们:

  • 声明每个堆分配应该放在try..catch块中,以检查OutOfMemoryException错误 - 记录到磁盘/发送到syslog服务器/等。
  • 检查每个变量分配只是为了确保“需要” - 如果需要,分配两次。
  • For循环不应该被信任,因为一旦你看到一个“错过了一步” - 所有循环都是使用gotos。
  • 存储每个字符串的SHA1哈希值。在更改软件“安全”部分中的字符串值时比较/更新哈希值 - 以确保不会无意中更改字符串。
  • 通过强制转换为浮点数并使用epsilon进行比较来执行整数相等性测试,因为您曾经听说过一个大值为2的故事导致[插入最近的核电站]发生重大事故。

如果他们看不到其中一些测试只是有点疯狂,那就没有希望了。

答案 13 :(得分:1)

我刚才写了一些关于防御性编程的文章:

http://www.francisfish.com/what_defensive_programming_is_and_isnt_logging_the_right_t.htm

我认为上面关于强迫人们测试所有代码路径的建议是非常有效的,并且如果他们是人类的话会有效。

答案 14 :(得分:1)

每行代码都是错误的机会。编写不影响程序行为的代码行会增加错误而没有任何好处。

我会等到一个直接归因于这样的代码的bug出现,然后再次争论我的情况。手头的证据就容易多了。

答案 15 :(得分:0)

  • 如果他们这么担心的话 那时候不会第一次工作 他们应该重新写一些东西 直到他们确信它会起作用。

  • 如果有合理的机会 有些东西第一次不起作用 圆的,他们没有什么可以做的 做提高它的机会 工作,那么正确的解决方案是 使用try / catch - 而不仅仅是调用 它两次。

答案 16 :(得分:-1)

数小时后偷走他们RAM的一半。如果每个无意义代码之间需要花费一分钟左右的时间,那么在编译和运行它们之后它们会得到失败消息,他们不禁会想到为什么会出现这些问题。