Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
File-Based Integration: Exchanging Files over SFTP and FTP, Watching Folders, Idempotent Processing and Reconciliation
In a file-based integration you read a file only once the other side has marked it ready (a .done marker, or upload under a temporary name and rename), verify the host key, and record processed files in a ledger by content hash, so nothing is processed half-written or twice. In Bookstore we fetch the supplier's stock file over SFTP with SSH.NET and the bank's collections file over FTPS with FluentFTP, and reconcile it against orders.
Calling SOAP Services from .NET: dotnet-svcutil, the WCF Client, WS-Security, Certificates and Resilience
You call a SOAP service from .NET through the interface dotnet-svcutil generates from the WSDL, your own binding and a ChannelFactory; resilience belongs at the operation level, not the HTTP level, because SOAP faults arrive with HTTP 500 and WCF calls take no CancellationToken. In Bookstore we send institutional invoices to a fake e-invoice integrator with a UsernameToken and mTLS, and make retries safe with the invoice's ETTN.
State Across Requests in MVC: TempData, Session, Cookies and Query Strings — Which One When?
Use the query string for filters, a consented cookie for preferences, a Data Protection cookie for an anonymous cart, session with Redis for a multi-step form and TempData for flash messages. Running Bookstore.Web on two instances, we show with real output how in-memory session silently resets the form, how separate key rings empty the cart and how cookie consent switches off TempData.
Error Pages in MVC and Razor Pages: UseExceptionHandler, Status Code Pages, Custom 404/500 Views and Error Ids
For pages, use UseExceptionHandler and UseStatusCodePagesWithReExecute: the status code is kept and the address doesn't change, while WithRedirects produces a soft 404 with a 302 and a 200. In Bookstore.Web we show a 404 with suggestions, a 503 page with a trace id, ProblemDetails for /api, IExceptionHandler for domain exceptions and the pitfalls where the error page itself breaks, with real output.
Data Access Options in Node.js: node:sqlite, pg, a Query Builder (Kysely) and an ORM (Prisma) — Which One When?
Data access in Node.js comes in four layers: the built-in node:sqlite for tests and tools, plain SQL with pg for full control, Kysely for compile-time type safety, and Prisma for schemas and migrations. With real output: prices concatenated because numeric arrives as a string, text stored in an INTEGER column, a limit that would not run on SQL Server, a relation difference caught by migrate diff, and an N+1 loop running five queries for four books.
CPU-Bound Work with worker_threads: Workers, MessageChannel, Transferables, Worker Pools (Piscina) and How It Differs from cluster
In Node.js you move CPU-bound work to worker_threads, preferably a worker pool such as Piscina, so the event loop never freezes; large data is transferred as an ArrayBuffer instead of copied, and the worker returns only a summary. With real output: a 947 ms event loop stall versus 11 ms, an 11 ms copy versus a zero-copy transfer, a pooled Buffer that cannot be transferred, a counter losing updates without Atomics, and a new worker per task running 40 times slower.
Everyday Cryptography with node:crypto: randomUUID, Hashes, HMAC, Passwords with scrypt, timingSafeEqual and Web Crypto
In Node.js you verify a webhook signature with createHmac over the raw body and compare it with timingSafeEqual, store passwords with scrypt or the built-in Argon2id, and encrypt sensitive fields with AES-256-GCM and a fresh IV every time. With real output: a signature broken by JSON.stringify, SHA-256 at over two million per second versus 169 ms scrypt, 622 MB for 16 concurrent sign-ins, and a phone number recovered without the key from a fixed IV.
Writing Scripts and CLI Tools in Node.js: process, util.parseArgs, Exit Codes, readline, child_process and Signals
A Node.js CLI parses process.argv with util.parseArgs, writes results to stdout and errors to stderr, exits through process.exitCode instead of process.exit, and calls external commands without a shell through execFile or spawn. With real output: an export file never created because of process.exit, strict parseArgs catching a typo, a confirmation that does not ask without a terminal, a command injection through exec, and a script that deletes its temp file on SIGINT.
HTTP Clients in Node.js: fetch, undici, AbortSignal.timeout, Keep-Alive, Streaming Bodies and Retries
In Node.js you call external services with the global fetch, but it is not safe without a response.ok check, AbortSignal.timeout, a body that is read or cancelled, and retries that respect idempotency. With real output: a catch that never runs on a 404, a request waiting 301 seconds, a connection lost to an unread body, a file streamed in 131 MB instead of 329 MB, retry waves without jitter, and a carrier tracking client tested with MockAgent.
End to End: Bookstore Order Documents — Invoice PDF, Confirmation Email and the Monthly Excel Report as a Job Chain
Connect the order documents in a two-step Hangfire chain: the invoice job writes the PDF to storage, the continuation puts the confirmation email in the outbox; each step's key is the very thing it writes, so repeats are harmless. In Bookstore only the invoice step was retried while storage was down, the same order placed twice gave one PDF and one email, and the monthly report is a recurring job on Istanbul time.
A File Storage Abstraction: Local Disk, Azure Blob Storage and S3; Direct Uploads with Presigned/SAS URLs
Keep files behind one IFileStore: validate the path twice on the local disk, hand out SAS or presigned URLs that live a few minutes for downloads and uploads on Azure Blob and S3, and check an uploaded file only in the "complete" step, because storage looks at neither size nor content. Bookstore's invoices and covers were tested with Azurite and RustFS; metadata stays in the database.
Image Processing in .NET: Resizing, Format Conversion, EXIF and Safe Processing with ImageSharp and SkiaSharp
Validate an uploaded image by its first bytes and pixel count first, then apply the EXIF orientation, shrink it and save it as WebP without metadata. Bookstore's cover thumbnails are made with SkiaSharp (MIT); ImageSharp 4 stays in a Debug-only example, because its Release build needs a license key and it keeps metadata by default.