views:

233

answers:

9

I develop and maintain a large (500k+ LOC) WinForms app written in C# 2.0. It's multi-user and is currently deployed on about 15 machines. The development of the system is ongoing (can be thought of as a perpetual beta), and there's very little done to shield users from potential new bugs that might be introduced in a weekly build.

For this reason, among others, i've found myself becoming very reliant on edit-and-continue in the debugger. It helps not only with bug-hunting and bug-fixing, but in some cases with ongoing development as well. I find it extremely valuable to be able to execute newly-written code from within the context of a running application - there's no need to recompile and add a specific entry point to the new code (having to add dummy menu options, buttons, etc to the app and remembering to remove them before the next production build) - everything can be tried and tested in real-time without stopping the process.

I hold edit-and-continue in such high regard that I actively write code to be fully-compatible with it. For example, I avoid:

  • Anonymous methods and inline delegates (unless completely impossible to rewrite)
  • Generic methods (except in stable, unchanging utility code)
  • Targeting projects at 'Any CPU' (i.e. never executing in 64-bit)
  • Initializing fields at the point of declaration (initialisation is moved to the constructor)
  • Writing enumerator blocks that use yield (except in utility code)

Now, i'm fully aware that the new language features in C# 3 and 4 are largely incompatible with edit-and-continue (lambda expressions, LINQ, etc). This is one of the reasons why i've resisted moving the project up to a newer version of the Framework.

My question is whether it is good practice to avoid using these more advanced constructs in favor of code that is very, very easy to debug? Is there legitimacy in this sort of development, or is it wasteful? Also, importantly, do any of these constructs (lambda expressions, anonymous methods, etc) incur performance/memory overheads that well-written, edit-and-continue-compatible code could avoid? ...or do the inner workings of the C# compiler make such advanced constructs run faster than manually-written, 'expanded' code?

+15  A: 

Without wanting to sound trite - it is good practice to write unit/integration tests rather than rely on Edit-Continue.

That way, you expend the effort once, and every other time is 'free'...

Now I'm not suggesting you retrospectively write units for all your code; rather, each time you have to fix a bug, start by writing a test (or more commonly multiple tests) that proves the fix.

As @Dave Swersky mentions in the comments, Mchael Feathers' book, Working Effectively with Legacy Code is a good resource (It's legacy 5 minutes after you wrote it, right?)

So Yes, I think it's a mistake to avoid new C# contructs in favor of allowing for edit and continue; BUT I also think it's a mistake to embrace new contructs just for the sake of it, and especially if they lead to harder to understand code.

Mitch Wheat
+100 if I could. Writing code specifically to support edit-and-continue while not using unit tests is, to put it mildly, not the best approach IMHO. May I also recommend the book *Working Effectively With Legacy Code*: http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052
Dave Swersky
I will add that most of the bugs we're seeing at this late stage of the project are GUI and cross-unit/integration related, rather than occurring within the confines of a single business object. This is very much a maintaining-and-enhancing stage. If it were possible to unit test the GUI components, believe me, I would!
Bradley Smith
+1: Edit and Continue is a complete *non-issue* for unit tests.
Craig Stuntz
A: 

You could try Test Driven Development. I found it very useful to avoid using the debugger at all. You start from a new test (e.g. unit test), and then you only run this unit test to check your development - you don't need the whole application running all the time. And this means you don't need edit-and-continue!

I know that TDD is the current buzz-word, but it really works for me. If I need to use the debugger I take it as a personal failure :)

Grzenio
+4  A: 

Wanted to clarify these things a bit

it is good practice to avoid using these more advanced constructs in favor of code that is very, very easy to debug?

Edit and Continue is not really debugging, it is developing. I make this distinction because the new C# features are very debuggable. Each version of the language adds debugging support for new language features to make them as easy as possible debug.

everything can be tried and tested in real-time without stopping the process.

This statement is misleading. It's possible with Edit and Continue to verify a change fixes a very specific issue. It's much harder to verify that the change is correct and doesn't break a host of other issues. Namely because edit and continue doesn't modify the binaries on disk and hence doesn't allow for items such as unit testing.

Overall though yes I think it's a mistake to avoid new C# contructs in favor of allowing for edit and continue. Edit and Continue is a great feature (really loved it when I first encountered it in my C++ days). But it's value as a production server helper doesn't make up for the producitivy gains from the new C# features IMHO.

JaredPar
+1  A: 

You should really introduce continues integration, which can help you to find and eliminate bugs before deploying software. Especially big projects (I consider 500k quite big) need some sort of validation.

http://www.codinghorror.com/blog/2006/02/revisiting-edit-and-continue.html

Regarding the specific question: Don't avoid these constructs and don't rely on your mad debugging skills - try to avoid bugs at all (in deployed software). Write unit tests instead.

atamanroman
+4  A: 

My question is whether it is good practice to avoid using these more advanced constructs in favor of code that is very, very easy to debug

I would argue that any time you are forcing yourself to write code that is:

  1. Less expressive
  2. Longer
  3. Repeated (from avoiding generic methods)
  4. Non-portable (never debug and test 64bit??!?!?)

You are adding to your overall maintenance cost far more than the loss of the "Edit and Continue" functionality in the debugger.

I would write the best code possible, not code that makes a feature of your IDE work.

Reed Copsey
'best' meaning easiest to understand I hope!
Mitch Wheat
+1  A: 

I've also worked on very large permanent-beta projects.

I've used anonymous methods and inline delegates to keep some relatively simple bits of use-one logic close to their sole place of use.

I've used generic methods and classes for reuse and reliability.

I've initialised classes in constructors to as full an extent as possible, to maintain class invariants and eliminate the possibility of bugs caused by objects in invalid states.

I've used enumerator blocks to reduce the amount of code needed to create an enumerator class to a few lines.

All of these are useful in maintaining a large rapidly changing project in a reliable state.

If I can't edit-and-continue, I edit and start again. This costs me a few seconds most of the time, a couple of minutes in nasty cases. Its worth it for the hours that greater ability to reason about code and greater reliability through reuse saves me.

There's a lot I'll do to make it easier to find bugs, but not if it'll make it easier to have bugs too.

Jon Hanna
A: 

Relying on Edit and Cont. sounds as if there is very little time spent on designing new features, let alone unit tests. This I find to be bad because you probably end up doing a lot of debugging and bug fixing, and sometimes your bug fixes cause more bugs, right?

However, it's very hard to judge whether you should or should not use language features or not, because this also depends on many, many other factors : project reqs, release deadlines, team skills, cost of code manageability after refactoring, to name a few.

Hope this helps!

Jas
+1  A: 

While there is nothing inherently wrong with your approach, it does limit you to the amount of expressiveness understood by the IDE. Your code becomes a reflection of its capabilities, not the language's, and thus your overall value in the development world decreases because you are holding yourself back from learning other productivity-enhancing techniques. Avoiding LINQ in favor of Edit-and-Continue feels like an enormous opportunity cost to me personally, but the paradox is that you have to gain some experience with it before you can feel that way.

Also, as has been mentioned in other answers, unit-testing your code removes the need to run the entire application all the time, and thus solves your dilemma in a different way. If you can't right-click in your IDE and test just the 3 lines of code you care about, you're doing too much work during development already.

Bryan Watts
I'll admit that I come from a slightly dated, academic background where 'unit testing' was taught as an idea/process rather than something which could be applied using the vast suite of tools available in VS these days. It's too late for this project - the stakeholders don't recognise the need for it and we're too close to the end for it to make a real difference. Good advice for next time!
Bradley Smith
A: 

The issue you seem to be having is:

It takes too long to rebuild you app, start it up again and get to the bit of UI you are working on.

As everyone has said, Unit Tests will help reduce the number of times you have to run your app to find/fix bugs on none UI code; however they don’t help with issues like the layout of the UI.

In the past I have written a test app that will quickly load the UI I am working on and fill it with dummy data, so as to reduce the cycle time.

Separating out none UI code into other classes that can be tested with unit tests, will allow you do use all C# constructs in those classes. Then you can just limit the constructs in use in the UI code its self.

When I started writing lots of unit tests, my usage of “edit-and-continue” went down, I how hardly use it apart from UI code.

Ian Ringrose