This episode covers handling failures well: the try-catch-finally pattern, throw and custom exceptions, resource management with the using declaration, debugging techniques in Visual Studio and VS Code, and basic logging for production applications.

A long-running application is bound to face failures: a broken database connection, a missing file, invalid user input. The difference between a professional application and a fragile one lies in how these failures are handled.
Exception handling isn't about covering up errors — it's about deciding where failures may occur, how they are dealt with, and how failure information is recorded to be fixed later.
Episode 6 teaches the try-catch-finally pattern, how to create and throw custom exceptions, resource management with using, debugging techniques in Visual Studio and VS Code, and basic logging that will carry over to episode 20.
The try block holds code that might fail, catch handles a specific exception type, and finally always runs for cleanup:
try
{
var pembagi = 0;
var hasil = 10 / pembagi;
Console.WriteLine(hasil);
}
catch (DivideByZeroException)
{
Console.WriteLine("Tidak bisa membagi dengan nol.");
}
finally
{
Console.WriteLine("Blok ini selalu berjalan.");
}Catch exceptions as specifically as possible. catch (DivideByZeroException) handles a specific case, while a plain catch (Exception) catches everything — use it with care because it can hide real bugs.
When your application domain has its own characteristic errors, create your own exception by inheriting from Exception:
class StokTidakCukupException : Exception
{
public StokTidakCukupException(string namaProduk, int sisa)
: base($"Stok {namaProduk} tidak cukup, sisa {sisa}.") { }
}
void KurangiStok(string produk, int jumlah)
{
throw new StokTidakCukupException(produk, 0);
}
try
{
KurangiStok("Kopi", 5);
}
catch (StokTidakCukupException ex)
{
Console.WriteLine(ex.Message);
}Custom exceptions use the base constructor pattern shown above, so error messages stay consistent and can be inspected at the application's top level.
Resources like files and database connections must be released. Since C# 8, the using declaration releases resources automatically at the end of the scope:
using var writer = new StreamWriter("catatan.txt");
writer.WriteLine("Baris pertama");
writer.WriteLine("Baris kedua");When execution leaves the method, the StreamWriter object is disposed automatically by the compiler. No manual finally, no leaked resources. We'll dive deeper into file I/O details in episode 7.
The debugger lets you pause execution at a specific line and inspect variable values. In VS Code, the debug config is stored in the .vscode/launch.json file:
{
"version": "0.2.0",
"configurations": [
{
"name": "C# Console",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/net8.0/Aplikasi.dll",
"args": [],
"cwd": "${workspaceFolder}"
}
]
}The "request": "launch" configuration above starts the program in debug mode with breakpoints active. In Visual Studio, press F9 to mark a breakpoint and F5 to start debugging.
Writing to the console with Console.WriteLine isn't enough for production applications. Use the ILogger abstraction, which provides log levels and structured formatting:
using var loggerFactory = LoggerFactory.Create(b =>
b.AddConsole().SetMinimumLevel(LogLevel.Information));
var logger = loggerFactory.CreateLogger("Aplikasi");
logger.LogInformation("Aplikasi mulai pada {Waktu}", DateTime.Now);
logger.LogWarning("Stok produk {Produk} menipis.", "Kopi");
logger.LogError("Gagal terhubung ke database.");Placeholders like {Waktu} make logs structured — not just text — so they can be queried later. Log levels from lowest to highest: Trace, Debug, Information, Warning, Error, Critical.
Key takeaways:
finally for mandatory cleanup.Exception with consistent messages.using declaration releases resources automatically.ILogger beats Console.WriteLine.In the next episode 7 we deal with data outside memory: input/output and file handling — System.IO with File, Directory, and Path, streams and StreamReader/Writer, JSON and XML serialization, and the concept of asynchronous I/O with async/await.