Every programming runtime possesses unique failure modes—from JavaScript compile-time type erasure to Python mutable defaults, C# async deadlocks, and Go goroutine leaks. Reviewers should use these focused recipes to quickly inspect PRs for subtle concurrency bugs, memory leaks, and runtime injection risks.
Casting untrusted JSON payloads to TypeScript interfaces without runtime verification causes silent runtime crashes.
❌ VULNERABLE / SUBOPTIMAL
// ❌ VULNERABLE: TypeScript interface disappears at runtime
export async function handleWebhook(req: Request) {
const data = (await req.json()) as { userId: string; amount: number };
// If data.amount is undefined or malicious string, calculation fails silently!
return processPayment(data.userId, data.amount * 100);
}
✅ SECURE / IDIOMATIC
// ✅ SECURE: Runtime schema parsing with Zod guarantees safety
import { z } from 'zod';
const WebhookPayloadSchema = z.object({
userId: z.string().uuid(),
amount: z.number().positive(),
});
export async function handleWebhook(req: Request) {
const json = await req.json();
const result = WebhookPayloadSchema.safeParse(json);
if (!result.success) {
return new Response(JSON.stringify({ error: result.error.format() }), { status: 400 });
}
return processPayment(result.data.userId, result.data.amount * 100);
}
Why this matters: TypeScript types are erased at compile time. Runtime boundary validation is mandatory for all external I/O in regulated applications.
Pitfall 2: Unhandled Promise Floating in Background Loops
Firing async functions inside Array.map or forEach without Promise.all causes uncaught background exceptions and race conditions.
❌ VULNERABLE / SUBOPTIMAL
// ❌ BUG: forEach does not await async callbacks; executes out of order
async function syncAuditLogs(entries: LogEntry[]) {
entries.forEach(async (entry) => {
await db.auditLogs.insert(entry);
});
console.log('All logs synced!'); // Fires before inserts finish!
}
✅ SECURE / IDIOMATIC
// ✅ CORRECT: Promise.all with sequential or bounded parallel execution
async function syncAuditLogs(entries: LogEntry[]) {
await Promise.all(entries.map((entry) => db.auditLogs.insert(entry)));
console.log('All logs verified and synced!');
}
Why this matters: Array.forEach ignores returned promises. Use Promise.all, Promise.allSettled, or a for...of loop to ensure all operations finish reliably.
Are default function arguments immutable (avoiding `def fn(items=[])`)?
Are file handles and database connections wrapped in `with` context managers?
Are Pydantic models or `typing` annotations strictly enforced?
Are SQL queries executed via ORM parameters rather than f-strings?
Critical Pitfalls & Code Comparisons
Pitfall 1: Mutable Default Argument State Leak
Default arguments in Python are evaluated once at function definition time, sharing state across all subsequent invocations.
❌ VULNERABLE / SUBOPTIMAL
// ❌ VULNERABLE: Default list is shared across all calls and users!
def add_patient_record(patient_id: str, tags: list = []):
tags.append(patient_id)
return tags
# Call 1: add_patient_record("P1") -> ["P1"]
# Call 2: add_patient_record("P2") -> ["P1", "P2"] (LEAKS PREVIOUS PATIENT ID!)
✅ SECURE / IDIOMATIC
// ✅ SECURE: Use None as default and instantiate freshly inside function
from typing import Optional, List
def add_patient_record(patient_id: str, tags: Optional[List[str]] = None) -> List[str]:
if tags is None:
tags = []
tags.append(patient_id)
return tags
Why this matters: Always use `None` as the default sentinel value for mutable arguments to prevent critical cross-session data contamination.
Pitfall 2: SQL Injection via Raw f-Strings
Constructing SQL queries with string interpolation bypasses database escaping and creates critical SQL injection flaws.
Are all `IDisposable` resources managed via `using` statements or dependency injection?
Are async tasks awaited properly without calling `.Result` or `.Wait()` (preventing thread pool deadlocks)?
Are LINQ queries evaluated deliberately (avoiding multiple DB round-trips with `.ToList()`)?
Is `#nullable enable` active with clean compiler warnings?
Critical Pitfalls & Code Comparisons
Pitfall 1: Async Deadlock via `.Result` Blocking
Calling `.Result` or `.Wait()` on an async Task inside synchronous methods can cause deadlocks on synchronization contexts.
❌ VULNERABLE / SUBOPTIMAL
// ❌ DEADLOCK RISK: Synchronously blocking on async task
public IActionResult GetMedicalRecord(string id)
{
// Can cause thread pool starvation and deadlocks in ASP.NET Core!
var record = _recordService.FetchRecordAsync(id).Result;
return Ok(record);
}
✅ SECURE / IDIOMATIC
// ✅ ASYNC ALL THE WAY: Proper async/await cascade
public async Task<IActionResult> GetMedicalRecord(string id)
{
var record = await _recordService.FetchRecordAsync(id);
return Ok(record);
}
Why this matters: Never block on asynchronous code with `.Result` or `.GetAwaiter().GetResult()`. Maintain asynchronous execution from controller to database.
Pitfall 2: Unreleased Unmanaged Resources without `using`
Streams, cryptographic contexts, and database connections that omit `using` statements leak unmanaged OS handles under load.
❌ VULNERABLE / SUBOPTIMAL
// ❌ RESOURCE LEAK: Stream not disposed if exception occurs
public byte[] EncryptPayload(byte[] data, byte[] key)
{
var aes = Aes.Create();
var encryptor = aes.CreateEncryptor(key, aes.IV);
var ms = new MemoryStream();
// If exception thrown here, MemoryStream & Aes handles leak!
return ms.ToArray();
}
✅ SECURE / IDIOMATIC
// ✅ CLEAN: C# 8+ using declarations guarantee disposal
public byte[] EncryptPayload(byte[] data, byte[] key)
{
using var aes = Aes.Create();
using var encryptor = aes.CreateEncryptor(key, aes.IV);
using var ms = new MemoryStream();
return ms.ToArray();
}
Why this matters: C# `using var` declarations automatically invoke `.Dispose()` when scope exits, ensuring zero resource leakage even during runtime faults.
Do all goroutines have guaranteed termination conditions via `context.Context`?
Are errors explicitly wrapped with `%w` for audit traceability?
Are mutex locks paired immediately with `defer mu.Unlock()`?
Are HTTP response bodies explicitly closed with `defer resp.Body.Close()`?
Critical Pitfalls & Code Comparisons
Pitfall 1: Leaking Goroutines on Unbuffered Channels
Sending to an unbuffered channel when no receiver is listening blocks the goroutine indefinitely, leaking memory.
❌ VULNERABLE / SUBOPTIMAL
// ❌ LEAK: Goroutine hangs forever if context cancels before send!
func QueryRecord(ctx context.Context) (*Record, error) {
ch := make(chan *Record)
go func() {
rec := slowDatabaseFetch()
ch <- rec // BLOCKS INDEFINITELY if ctx cancelled!
}()
select {
case <-ctx.Done():
return nil, ctx.Err()
case res := <-ch:
return res, nil
}
}
✅ SECURE / IDIOMATIC
// ✅ SAFE: Buffered channel allows goroutine to finish and exit
func QueryRecord(ctx context.Context) (*Record, error) {
ch := make(chan *Record, 1) // Buffer size 1 prevents blocking
go func() {
rec := slowDatabaseFetch()
ch <- rec
}()
select {
case <-ctx.Done():
return nil, ctx.Err()
case res := <-ch:
return res, nil
}
}
Why this matters: Always use a buffered channel of size 1 for one-shot async background workers so the goroutine can exit even if the caller times out.
Pitfall 2: Silent Error Swallowing without `%w` Context
Returning generic error messages loses stack context and root-cause traceability required for regulatory incident investigation.
// ✅ TRACEABLE: Wrapped error preserves root cause for auditing
import "fmt"
func DecryptRecord(data []byte) ([]byte, error) {
decrypted, err := cipher.Open(nil, nonce, data, nil)
if err != nil {
return nil, fmt.Errorf("decrypting medical record payload: %w", err)
}
return decrypted, nil
}
Why this matters: Using `fmt.Errorf("...: %w", err)` preserves root-cause errors for `errors.Is()` and `errors.As()` while maintaining audit traceability.
Try This With AI: Language Idiom & Concurrency Reviewer
Language Review Prompt
Use this prompt to spot language-specific memory leaks, async deadlocks, and type safety breaches in your PR diffs:
"Act as a Principal Polyglot Software Architect. Review the following code snippet written in [LANGUAGE: TypeScript / Python / C# / Go]: [PASTE CODE SNIPPET]. Analyze for: (1) Concurrency, async deadlock, or memory leak risks, (2) Unsafe type casting or missing runtime boundary validation, (3) Resource disposal omissions (unclosed streams / contexts), and (4) Refactor the code to follow idiomatic production best practices."
Community Discussion & Feedback
Attributed peer feedback and official Netspective architecture notes.
Was this documentation helpful?(100% found this helpful • 0 ratings)