Skip to content
Back to skills

Minimal

ASecurity

Choose how services talk to each other, given that every synchronous call adds latency and borrows the callee's availability.

  • 2 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 10, 2026
databasespythonreactdatabase

Security analysis

A100/100

Scanned September 10, 2026

npx -y skills add snoodleboot-io/prompticorn --skill minimal --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Minimal?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Minimal
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/snoodleboot-io-minimal-c0133dce/badge)](https://www.skillsdirectory.com/skills/snoodleboot-io-minimal-c0133dce)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
# Microservices Communication Patterns (Minimal)

## Purpose
Choose how services talk to each other, given that every synchronous call adds latency and borrows the callee's availability.

## Core Techniques

### 1. Do the Availability Arithmetic

A synchronous chain multiplies. Five services at 99% each, called in series, yield 0.99^5 ≈ 95.1% — roughly 36 hours of downtime a month, from components that each look healthy.

Latency composes the same way, and worse at the tail: five hops of p99 = 100 ms is not a 100 ms p99 end to end, because a request is slow if *any* hop is slow.

| Chain depth | Availability (99% each) | Downtime/month |
|---|---|---|
| 1 | 99.0% | 7.2 h |
| 3 | 97.0% | 21.6 h |
| 5 | 95.1% | 35.6 h |

This is the core argument for asynchrony: not throughput, availability.

### 2. Match the Pattern to the Question

| Pattern | Use when | Cost |
|---|---|---|
| Sync request/response | Caller needs the answer to proceed | Latency and availability coupling |
| Async command (queue) | Work must happen, caller need not wait | Requires idempotent consumers |
| Event publish/subscribe | Others may care that something happened | Eventual consistency |
| Request/reply over a broker | Long-running work with a result | Correlation and timeout handling |

Ask what the caller does with the response. If the answer is "nothing, it just logs it," the call should not be synchronous.

### 3. Publish Events, Do Not Send Commands Dressed as Events

```
# ✅ Event: a fact, past tense, no assumption about who listens
order.placed        {"order_id": "8812", "total_cents": 4200}

# ❌ Command in disguise: names the consumer and its behavior
order.send_confirmation_email_to_customer
```

The distinction is who owns the decision. An event says what happened; each consumer decides what that means for it. A command names a consumer, which means adding a second reaction requires changing the producer — you have kept the coupling and merely moved it onto a broker.

### 4. At-Least-Once Delivery Makes Idempotency Mandatory

Every real broker redelivers: after a consumer crash before ack, a rebalance, or a visibility timeout expiring on slow work. "Exactly once" end to end is not something the transport can give you — it is something the consumer implements by deduplicating.

```python
def handle(msg):
    if not claim(msg.id):     # INSERT ... ON CONFLICT DO NOTHING
        return                # already processed; ack and move on
    charge_card(...)
```

Use a stable, producer-assigned id, not a broker-generated one that changes on redelivery. See the `idempotency-patterns` skill for the full treatment.

### 5. Bound Every Synchronous Call

An unbounded call is how one slow dependency exhausts your thread pool and takes you down with it:

- **Timeout** shorter than your own caller's timeout, always set explicitly
- **Retry** only on idempotent operations, with exponential backoff and jitter — synchronized retries are how a recovering service gets knocked over again
- **Circuit breaker** so a failing dependency fails fast instead of consuming capacity
- **Bulkhead** — a separate connection pool per dependency, so one slow callee cannot starve the rest

### 6. Know Where Async Only Hides the Coupling

Replacing a call with an event does not decouple anything if the caller still cannot proceed without the result. If the producer publishes and then polls for a status change, you have built a synchronous call with worse latency and no error path. Genuine decoupling means the producer's work is complete at publish time.

## Warning Signs

- A user request that fans out into a chain of four or more synchronous hops
- Any HTTP or gRPC call with no explicit timeout
- Retries on non-idempotent operations, or without jitter
- Consumers that assume each message is delivered exactly once
- Event names in the imperative, naming a specific consumer's action
- Services sharing a database table to avoid writing an interface
- A distributed transaction spanning services (no saga, no compensations)
- Event schemas with no version field and no tolerance for unknown fields

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.