Asserts notify you the instant one of your assumptions about the code is invalidated. Think of asserts as a form of documentation that gets verified at runtime. Running your code and not seeing any asserts gives you a shot of confidence that the code is probably behaving correctly.
Asserts can find bugs at the source of the problem, not just at the later symptom. The root cause of many bugs occurs long before problems are apparent in the program's output. But well-written asserts can launch a debugger at the exact moment when errors begin to happen, saving you hours of debugging time.
Asserts are not for error handling. Asserts help identify bugs so you can fix them, but you still need to write error handling code for those errors that inevitably slip through. In most situations, each assert should be immediately followed by code to deal with the problem identified by that assert, just in case it ever comes up in Release mode.
Asserts normally run only in Debug mode. Remember any code inside an assert will be compiled away to nothingness in Release mode. You can have asserts run in Release mode by using.NET's Trace class, but there's not much point in doing so.
Make sure each assert is a legitimate error. If you're asserting on conditions that are rare but legal, then you're doing something wrong. As assert should represent an honest-to-goodness error that needs to be investigated.
Be careful using asserts in ASP.NET, distributed objects, or Windows services. Standard asserts will not work in these situations because they could potentially result in a process being blocked, since there may not be anyone on the remote machine available to acknowledge the assert.
You can write your own TraceListener class to customize asserts in whatever way you like. This is one of the best ways to use asserts in ASP.NET or Windows services.