对抗发布产品中的调试代码
我在一家非常小的初创开发商店(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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
有几件事对我来说很突出:
我会查找一些敏捷方法和实践。它的设计目的是考虑到版本中快速移动的目标。
您应该执行以下操作:
通过遵循任务/功能分支,您不必创建将像问题中那样签入的中间代码。那不应该发生。如果您切换将在另一个分支中的任务。使您的流程最小化,并且您的分支毫不费力。那么这些事情就会成为第二天性。
A few things stand out to me:
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:
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.
假设您的代码是 C# 语言,您可以使用
[Conditional ]
属性来排除 Release 配置中的某些方法调用:这相当于将方法本身及其所有调用包装到
#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: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 whenDEBUG
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?
分布式版本控制系统使分支和合并变得轻而易举。使用像 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.
我也有同样的情况。我有时会在新函数的调用周围加上#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.