对抗发布产品中的调试代码

发布于 2024-10-19 10:07:10 字数 1039 浏览 1 评论 0原文

我在一家非常小的初创开发商店(3 名开发人员)工作,经常被从我正在从事的工作中抽离出来,以修复关键任务错误或实现“绝对关键”的软件功能 - 我的错误列表被重新优先考虑几乎每天都会发生,我很少知道几个小时后什么会是“重要的”。

正因为如此,我发现我对添加可能未经我们 QA 部门(阅读:人)检查的代码变得越来越谨慎。

当我实现一个新函数时,不知道是否会在任何时候被调用,我有时会尝试在顶部编写一个 return 语句,以确保代码永远不会在发布版本中执行。

像这样的语句的问题

public void NewFunction()
{
    return; // Put break point so that I can use the debugger to step to meat:

    meat:
    // ... More code
}

是,即使在调试模式下,Visual Studio 也足够聪明,知道肉类:永远不会被执行,所以你不能对肉类使用“设置下一个语句”命令。将代码包装在#if !DEBUG 等编译器指令中也是如此。

相反,我会写这样的内容:

public void NewFunction()
{
    if("a"[0]=='a')
        return; // put break point here

    meat:
    // ... More code
}

这样,如果此代码意外滑入发行版,也不会造成任何损害,但因为直到运行时才对其进行评估,所以我可以使用调试器逐步解决问题:没有问题。

我真的不喜欢让事情未完成,但在关键时刻我们常常没有时间搁置变更集或正确地做事情。目前,通过 API 访问未完成的功能(如上面概述的功能)并不是一个问题,因此我认为将它们保留在软件中不会立即造成任何损害(但最终,它们可能会导致在使用过程中出现一些糟糕的时刻)维护 - 就像“wtf 这个函数在这里什么都不做吗?因为我不记得 6 个月后我在做什么”。

考虑到我(不幸的是)半定期地做这样的事情,这个问题有标准做法吗?作为一个更大的问题,是否有一些实践可以帮助解决一般软件版本中的调试代码?

I work in a very small startup development shop (3 developers), and am often pulled away from what I am working on to fix a mission critical bug or implement an "absolutely critical" software feature - my bug list is re-prioritized on an almost daily basis and I seldom know what will be "important" a a few hours from now.

Because of this, I have found that I am becoming more and more wary of adding code that can potentially go unchecked by our QA department (read: person).

When I implement a new function, not knowing whether I'll be called away from it at any time, I sometimes try to write a return statement at the top just to make sure that the code never gets executed on a release build.

The problem with statements like:

public void NewFunction()
{
    return; // Put break point so that I can use the debugger to step to meat:

    meat:
    // ... More code
}

are that even in debug mode, Visual Studio is smart enough to know that meat: will never be executed, so you can't use the "set next statement" command to meat:. The same is true for wrapping code in compiler directives like #if !DEBUG.

Instead, I write things like:

public void NewFunction()
{
    if("a"[0]=='a')
        return; // put break point here

    meat:
    // ... More code
}

so that if this code accidentally slips into a release, no harm is done, but because it's not evaluated until run time, I can use the debugger to step to meat: with no problem.

I don't really like leaving things unfinished, but in crunch times we often don't have time to shelve change sets or do things properly. Access to unfinished features like the function outlines above via an API aren't a concern at the moment, so I don't see any immediate harm in leaving them in the software (but down the line, they can lead to several wtf moments during maintenance - as in "wtf is this function that doesn't do anything doing here?" because I won't remember what I was doing 6 months from now).

Considering that I am (sadly) doing stuff like this on a semi-regular basis, is there a standard practice for this issue? As a larger issue, is there some set of practices that help to combat debug code in software releases in general?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

青衫负雪 2024-10-26 10:07:10

有几件事对我来说很突出:

  • “我真的不喜欢让事情未完成”,
  • “我有时会尝试在顶部编写一个 return 语句,只是为了确保代码永远不会在发布版本上执行。”
  • “这样,如果这段代码不小心滑入版本中”,

我会查找一些敏捷方法和实践。它的设计目的是考虑到版本中快速移动的目标。

您应该执行以下操作:

  • 确保所有新代码都有单元测试来支持其功能。
  • 为您更改的代码编写单元测试,以确保您更改的内容不会破坏现有功能。
  • 使用 Cruise Control 或类似产品执行连续构建。这与测试一起让您在出现问题时立即知道。
  • 开始按功能/任务进行分支,这样就不会“意外”出现代码。在所有代码的主线之外工作,即使您从一个优先任务转移到另一个优先任务也会导致这些失误。
  • 产品应该始终有效。在任何时间点,只要产品中完成了 X 个功能,它就应该可以工作。不要半途而废地实现某个功能。将其分解为可以说有效的更小的功能和任务。即使客户永远不会使用它们也没关系。这可以防止未完成的功能被其他开发人员使用并破坏。
  • 修复破损的窗户。不要让非常糟糕的设计或错误继续存在。这只会使问题变得更加复杂。

通过遵循任务/功能分支,您不必创建将像问题中那样签入的中间代码。那不应该发生。如果您切换将在另一个分支中的任务。使您的流程最小化,并且您的分支毫不费力。那么这些事情就会成为第二天性。

A few things stand out to me:

  • "I don't really like leaving things unfinished,"
  • "I sometimes try to write a return statement at the top just to make sure that the code never gets executed on a release build."
  • "so that if this code accidentally slips into a release"

I would look up some Agile methodologies and practices. It was designed to take into account fast moving targets in the release.

You should do the following:

  • Ensure all your new code has unit tests backing up their functionality.
  • Write unit tests for code you change to ensure that what you changed didn't break existing functionality.
  • Perform continuous builds with Cruise Control or a similar product. This along with the tests will let you know immediately when something breaks.
  • Start branching by features/tasks so that no code "accidently" slips in. Working off the mainline for all of your code, even when you move from one priority task to another will lead to these slips.
  • The product should always work. At any point in time given X number of features that are completed in the product, it should work. Don't half implement a feature. Break it up into smaller features and tasks that you can say work. It doesn't matter if the customer will never use them. This prevents unfinished features that are then used by another developer and break.
  • Fix broken windows. Don't let very bad design or bugs continue to exist. It only compound the problem later.

By adhering to the task/feature branching you won't have to create intermediate code that will be checked in like you have in your question. That should not happen. If you switch tasks that will be in another branch. Make it so your process is minimal and your branching is effortless. Then these things will be second nature.

季末如歌 2024-10-26 10:07:10

假设您的代码是 C# 语言,您可以使用 [Conditional ] 属性来排除 Release 配置中的某些方法调用:

[Conditional("DEBUG")]
public void NewFunction (object something)
{
    // do something
}

这相当于将方法本身及其所有调用包装到 #if DEBUG / #endif 条款。

当未定义 DEBUG 符号时,任何对 NewFunction 的调用都将被编译器排除。控制它的最简单方法是通过 Visual Studio 中的解决方案和项目配置。在调试配置中,定义了DEBUG符号,这与释放配置相反。

请注意,只能排除 void 调用,因为代码不依赖于这些方法的返回值。

然而,这是设计用于跟踪、日志记录等。我不太明白你打算如何保留一些未完成的方法,但仍然在调试版本中使用它们。如果它们不起作用,那么调用它们有什么意义,或者如果它们起作用,为什么不将它们包含在发布中?

Assuming your code is in C#, you can use [Conditional] attribute to exclude certain method calls in Release configuration:

[Conditional("DEBUG")]
public void NewFunction (object something)
{
    // do something
}

This is equivalent to wrapping method itself and all its calls into #if DEBUG / #endif clauses.

Any calls to NewFunction will be excluded by compiler when DEBUG symbol is not defined. The simplest way to control it is by solution and project configurations in Visual Studio. In Debug configuration, DEBUG symbol is defined, as opposed to Relase configuraiton.

Note that only void calls can be excluded because code doesn't depend on return values for these methods.

However this is designed to be used for tracing, logging etc. I don't quite understand how you plan to leave some methods unfinished yet still use them in debug versions. What's the sense of calling them if they don't work, or if they do, why not include them in release?

自由如风 2024-10-26 10:07:10

分布式版本控制系统使分支和合并变得轻而易举。使用像 Mercurial 这样的工具(我个人的偏好),您甚至不需要远程存储库 - 您只需在本地系统上就地创建存储库,然后将该存储库克隆到本地驱动器上的其他位置。因此,您可以轻松保留代码的“发布”版本,同时快速将代码更改迁移到“调试”分支或从“调试”分支迁移。

Distributed version control systems make branching and merging a snap. With a tool like Mercurial (my personal preference), you don't even need a remote repository -- you just create the repo in-place on your local system, then clone the repo elsewhere on your local drive. So you can easily keep a "release" version of your code while rapidly migrating code changes to and from a "debug" branch.

一杯敬自由 2024-10-26 10:07:10

我也有同样的情况。我有时会在新函数的调用周围加上#IF DEBUG。或者#IF DEBUG 围绕函数的整个内容。

我做的另一件事是在函数上添加 TODO:Unit Test 注释,以便在发布时我可以搜索这些注释并知道是否存在问题。

I'm in the same situation. I sometimes put #IF DEBUG around the call to new function. Or #IF DEBUG around the entire contents of a function.

The other thing I do is put TODO:Unit Test comment on the function so that at release time I can search for those and know if there is a problem.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文