Provides guidance on concurrency and async patterns for TypeScript and Python applications. Covers structured concurrency, parallel execution, race condition prevention, backpressure, rate limiting, event loop internals, database locking strategies, and agent-safe concurrent operations. Use when implementing async workflows, parallelizing tasks, preventing race conditions, managing connection pools, rate-limiting API calls, or building concurrent agentic pipelines. Triggers: concurrency, async, parallel, race condition, deadlock, semaphore, mutex, backpressure, rate limit, event loop, worker threads, asyncio, TaskGroup, Promise.all, connection pool, optimistic locking.
Installation
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Structured over unstructured -- Every concurrent task must have a clear owner and bounded lifetime. Prefer TaskGroup/AbortController scoping over fire-and-forget. Dangling tasks are bugs.
Fail fast, cancel siblings -- When one task in a group fails, cancel the remaining siblings immediately. Use TaskGroup (Python) or Promise.all + AbortController (TS) to get this behavior.
Classify before parallelizing -- Separate read-only operations (safe to parallelize) from stateful/write operations (serialize or coordinate). This applies equally to database queries and agent tool calls.
Bound everything -- Every queue, pool, and concurrent operation needs an upper bound. Unbounded concurrency leads to resource exhaustion, OOM, and cascading failures.
Backpressure is mandatory -- If a producer can outpace a consumer, implement backpressure. Never rely on unbounded in-memory queues.
Measure contention -- Locks that are rarely contended are cheap. Locks that are always contended indicate a design problem. Profile before optimizing.
Workflow
Identify concurrency needs -- Determine whether the task is I/O-bound (use async), CPU-bound (use workers/processes), or mixed.
Choose the right primitive -- Select from the decision table below based on error handling, cancellation, and ordering requirements.
Set bounds -- Define maximum concurrency, queue depth, timeout, and retry limits before writing code.
Implement with structure -- Use structured concurrency (TaskGroup, AbortController scope) so tasks cannot outlive their parent.
Add synchronization -- Apply mutexes/semaphores only where shared mutable state requires coordination.
Test under load -- Simulate concurrent access, slow consumers, network failures, and partial failures.
Monitor in production -- Track queue depth, wait times, lock contention, and task completion rates.
import asyncio
# Structured concurrency -- all tasks complete or all cancelasyncdeffetch_all(urls: list[str]) -> list[bytes]:
results: list[bytes] = []
asyncwith asyncio.TaskGroup() as tg:
asyncdeffetch_one(url: str) -> None:
asyncwith aiohttp.ClientSession() as session:
asyncwith session.get(url) as resp:
results.append(await resp.read())
for url in urls:
tg.create_task(fetch_one(url))
return results # Only reached if ALL tasks succeed# Handle partial failures with gatherasyncdeffetch_tolerant(urls: list[str]):
results = await asyncio.gather(
*[fetch(url) for url in urls],
return_exceptions=True
)
successes = [r for r in results ifnotisinstance(r, Exception)]
failures = [r for r in results ifisinstance(r, Exception)]
return successes, failures
Maximum concurrency limits defined and enforced via semaphore or pool size
Structured concurrency used -- no orphaned tasks or dangling promises
Error handling strategy chosen (fail-fast vs partial-failure-tolerant)
Cancellation propagated -- AbortController (TS) or TaskGroup auto-cancel (Python)
Shared mutable state protected by mutex/lock or eliminated via message passing
Backpressure implemented for producer-consumer flows (bounded queues, stream highWaterMark)
Timeouts set on all async operations (network, locks, database queries)
Rate limits respected for external API calls (semaphore + delay)
Database access uses appropriate locking strategy (optimistic for reads-heavy, pessimistic for writes-heavy)
Connection pools sized appropriately (not too small to starve, not too large to exhaust)
Deadlock prevention verified -- consistent lock ordering, no nested locks where avoidable
Agentic tool calls classified as read-only (parallelize) or stateful (serialize)
Load tested under realistic concurrency levels with failure injection
Monitoring in place for queue depth, lock contention, task latency, and error rates
When to Escalate
Distributed concurrency -- When coordination spans multiple services or machines (distributed locks, consensus), escalate to infrastructure/platform team. Single-node primitives do not work across network boundaries.
Persistent deadlocks -- If deadlocks recur despite lock ordering, the resource dependency graph may need architectural redesign. Consult senior engineering.
GIL-bound Python performance -- If Python asyncio throughput is bottlenecked by the GIL for CPU work and ProcessPoolExecutor is insufficient, consider rewriting hot paths in Rust/C extensions or switching to a multi-process architecture.
Database contention at scale -- When optimistic locking retry rates exceed 5-10%, or pessimistic locks cause widespread blocking, the data model or access pattern needs rethinking with a database specialist.
Backpressure cascading failures -- If bounded queues cause upstream failures that propagate through the system, circuit breaker and load shedding patterns are needed at the architecture level.
Real-time ordering guarantees -- When strict global ordering of concurrent events is required (e.g., financial transactions), this requires distributed consensus (Raft, Paxos) or serializable isolation -- consult with the data team.