AI Coding Rules for Microservices
Encode microservices patterns as AI coding rules so every service follows the same boundary, communication, and observability standards from the start.
Why microservices need explicit AI rules
Monoliths have one codebase and one set of conventions. Microservices have dozens of codebases, each with their own runtime, dependencies, and deployment pipeline. When an AI assistant generates code in a monolith, most conventions are visible in the surrounding files. In a microservices architecture, the conventions that matter most are the ones that span services: how services communicate, where boundaries live, what health checks look like, how errors propagate.
Without explicit rules, every AI-generated service becomes a snowflake. One developer's service uses REST with JSON. Another uses gRPC. A third invents a custom message format over Redis pub/sub. The AI had no way to know your team settled on Protocol Buffers over gRPC for synchronous calls and CloudEvents over Kafka for async events. It just picked whatever fit the prompt.
This post covers the specific microservices patterns worth encoding as AI rules, with concrete examples you can adapt for Cursor, Claude Code, or any tool that supports rule files.
Service boundary rules
The hardest and most consequential decision in microservices is where to draw boundaries. Get it wrong and you end up with a distributed monolith: all the operational complexity of microservices with none of the independence.
AI assistants have no intuition about your domain model. They'll happily create a new service for every noun in a sentence. Your rules need to define what belongs together and what should stay separate.
## Service boundaries
- Each service owns exactly one bounded context. Do not split a bounded context
across multiple services.
- A service owns its data. No service reads or writes another service's database
directly. All cross-service data access goes through the service's public API.
- Before creating a new service, check if the functionality belongs in an existing
service. Prefer expanding a service over creating a new one.
- Shared types between services live in the `protos/` directory at the repo root,
not in any individual service.
The "prefer expanding over creating" rule is especially important for AI assistants. Left to their own defaults, they will generate a new service or module for any new feature request. Encoding a bias toward consolidation prevents service sprawl before it starts.
If you maintain AI rules in a monorepo, you can place boundary rules at the root level so they apply to all services. Package-level rules then handle the specifics of each service's stack.
Inter-service communication patterns
Communication between services is where most microservices teams accumulate the most inconsistency. Every service is built by a different developer at a different time, and without rules the AI will pick whatever communication pattern it encounters first in its training data.
Define both synchronous and asynchronous patterns explicitly:
## Inter-service communication
### Synchronous (request-response)
- Use gRPC for all service-to-service synchronous calls.
- Define service interfaces in `.proto` files under `protos/`.
- Generate client stubs using buf. Do not write HTTP clients manually.
- Set deadlines on every gRPC call. Default: 5 seconds. Override per-method
using gRPC service config or client-side configuration.
### Asynchronous (events)
- Use Kafka for all async event publishing.
- Events follow the CloudEvents v1.0 spec.
- Event schemas live in `schemas/events/` as JSON Schema files.
- Topic naming: `${domain}.${entity}.${action}`
(e.g., `orders.order.created`, `users.account.deleted`).
- Every event must include the CloudEvents required attributes: `specversion`,
`id`, `source`, `type`. Include `time` and `data` as a team convention.
### What NOT to do
- Do not use REST for service-to-service calls. REST is for external/public APIs only.
- Do not call services through a shared database, shared cache, or filesystem.
- Do not use synchronous calls for operations that can be eventual.
The "what NOT to do" section is critical. AI assistants default to REST for everything because that is what dominates their training data. Unless you explicitly tell them otherwise, they will generate fetch() calls to other services instead of using your gRPC stubs.
Shared schema and proto rules
In a microservices architecture, your .proto files and event schemas are the contract between services. If an AI assistant modifies a proto file incorrectly, the breakage ripples across every service that depends on it.
## Proto and schema rules
- Proto files use proto3 syntax. No proto2.
- Every proto message field must have a comment explaining its purpose.
- Never reuse or reassign field numbers. Deleted fields must be marked `reserved`.
- Backward compatibility is mandatory. You can ADD fields. You cannot REMOVE
or RENAME existing fields.
- Run `buf lint` and `buf breaking` before committing proto changes.
## Event schemas
- Event schemas use JSON Schema draft 2020-12.
- All fields in the `data` object must have `description` and `type`.
- New versions must be backward compatible with the previous version.
- Schema file naming: `${domain}.${entity}.${action}.v${version}.json`
These rules prevent the most common AI-generated mistakes in shared schemas: removing fields, changing types, and forgetting to mark deleted fields as reserved. A single broken proto change can take down multiple services in production.
Health checks and readiness probes
Every service in a microservices deployment needs health endpoints. Kubernetes, ECS, and other orchestrators depend on them to route traffic and restart unhealthy containers. But AI assistants will generate health check endpoints with wildly different shapes unless you specify a standard.
## Health checks
Every service must expose these HTTP endpoints (even if the service uses gRPC for
its main API):
### GET /healthz
- Returns 200 if the process is running.
- No dependency checks. This is a liveness probe.
- Response: `{ "status": "ok" }`
### GET /readyz
- Returns 200 only if all dependencies are reachable (database, cache, message broker).
- Returns 503 if any dependency is unhealthy.
- Response: `{ "status": "ready", "checks": { "db": "ok", "kafka": "ok" } }`
or `{ "status": "not_ready", "checks": { "db": "ok", "kafka": "failed" } }`
### GET /metrics
- Prometheus-compatible metrics endpoint.
- Use the `prom-client` library (Node.js) or `prometheus_client` (Python).
- Must include: request count, request duration histogram, error count,
active connections.
Standardized health checks are one of the highest-value rules you can write. They take five minutes to define and prevent hours of debugging when a service is deployed without proper readiness probes and starts receiving traffic before its database connection is established.
Observability rules
Microservices turn debugging from "read the logs" into "correlate traces across eight services." Without consistent observability patterns, diagnosing production issues becomes guesswork.
## Logging
- Use structured JSON logging. No plain text `console.log` in production code.
- Every log entry must include: `timestamp`, `level`, `service`, `traceId`, `message`.
- Log levels: `debug`, `info`, `warn`, `error`. Use them correctly:
- `info`: Request received, request completed, significant state changes
- `warn`: Recoverable errors, degraded functionality, retry attempts
- `error`: Unrecoverable errors, failed operations that need attention
- Do not log sensitive data: passwords, tokens, PII, credit card numbers.
## Distributed tracing
- Use OpenTelemetry for all tracing instrumentation.
- Propagate trace context on every outgoing request (gRPC metadata, Kafka headers).
- Create spans for: incoming requests, outgoing RPCs, database queries,
cache operations, message publish/consume.
- Span naming convention: `${service}.${operation}`
(e.g., `orders.createOrder`, `payments.processPayment`).
## Metrics
- Every service exports Prometheus metrics on /metrics.
- Standard metrics for all services (use middleware, not manual instrumentation):
- `http_requests_total` (labels: method, path, status)
- `http_request_duration_seconds` (histogram, labels: method, path)
- `grpc_server_handled_total` (labels: service, method, code)
- Business metrics specific to the service domain
The trace context propagation rule is particularly important. AI assistants will generate perfectly functional code that calls another service but forgets to pass along the traceparent header. When that happens, your distributed trace breaks into disconnected fragments and the whole tracing system becomes useless.
Circuit breakers and resilience
Microservices fail partially. A single slow or unresponsive service can cascade failures through the entire system if callers don't protect themselves. AI assistants will generate the happy path and skip resilience patterns unless instructed otherwise.
## Resilience patterns
### Circuit breakers
- Every outgoing service call must use a circuit breaker.
- Use the `opossum` library (Node.js) or `resilience4j` (Java/Kotlin).
- Circuit breaker settings (per-dependency, overridable):
- Failure threshold: 5 failures in 30 seconds opens the circuit
- Reset timeout: 30 seconds before half-open
- Timeout: match the gRPC deadline for that dependency
### Retries
- Retry transient failures (network errors, 503s) with exponential backoff.
- Maximum 3 retries with jitter.
- Do NOT retry 4xx errors. They are not transient.
- Idempotency: only retry operations that are safe to repeat.
POST requests that create resources need an idempotency key.
### Timeouts
- Every external call must have a timeout. No exceptions.
- Database queries: 5s default
- Service-to-service gRPC: 5s default (set via deadline)
- External third-party APIs: 10s default
- Message publish: 3s default
### Fallbacks
- Critical paths should have degraded-mode fallbacks.
- Example: if the recommendation service is down, show popular items instead
of returning an error to the user.
The retry rules need to be specific about what to retry and what not to. AI models tend to wrap every HTTP call in a generic retry loop. Without the "do not retry 4xx" rule, you end up with services hammering each other with requests that will never succeed.
Putting it all together: rule file structure
For a microservices project, organize your AI rules to match the architecture. Here is a structure that works across most setups:
my-platform/
CLAUDE.md # Root: org-wide conventions
.cursor/rules/
general.mdc # Shared coding standards
service-template.mdc # Service structure and patterns
communication.mdc # gRPC, Kafka, API patterns
observability.mdc # Logging, tracing, metrics
resilience.mdc # Circuit breakers, retries, timeouts
protos/ # Shared proto definitions
CLAUDE.md # Proto-specific rules
services/
orders/
CLAUDE.md # Order service context and domain rules
payments/
CLAUDE.md # Payment service context and domain rules
users/
CLAUDE.md # User service context and domain rules
Root-level rules handle cross-cutting concerns: communication patterns, observability standards, resilience requirements. Service-level rules handle domain-specific context: what this service does, what data it owns, what other services it depends on.
This layered approach mirrors the architecture itself. Cross-cutting concerns are standardized. Domain logic is local. See best practices for writing AI coding rules for more on structuring rules that scale.
Common mistakes to avoid
Writing rules that are too abstract. "Services should be loosely coupled" tells the AI nothing. "No service imports code from another service's src/ directory. Use the generated gRPC client from gen/" tells it exactly what to do.
Forgetting the negative rules. AI assistants are biased toward generating something, not avoiding something. If your team has explicitly decided against REST for internal communication, you need to say "do not use REST for service-to-service calls." A positive rule about using gRPC is not enough because the AI might use both.
Ignoring the local development story. Rules about Kafka and gRPC are useless if the AI doesn't know how to set up the local development environment. Include rules about docker-compose services, local ports, and how to run integration tests.
Not versioning your rules. Microservices architectures change. You adopt a new message broker, switch from REST to gRPC, add a service mesh. Your rules need to track these changes with the same discipline you apply to your code. Treat them as a dependency that gets versioned and updated deliberately.
For more on enforcing coding standards with AI assistants, including how to measure whether your rules are actually working, see our dedicated guide.
From rules to shared skills
Writing microservices rules for one team is a good start. Sharing them across your organization is where the real value compounds. When every team follows the same service template, the same communication patterns, and the same observability standards, your platform engineers can build tooling that works everywhere instead of handling special cases.
With localskills.sh, you can publish your microservices rules as versioned skills and install them across every service repository:
# Install org-wide microservices standards
localskills install your-org/microservices-base --target cursor claude windsurf
# Install domain-specific rules for a particular service
localskills install your-org/payment-service-rules --target cursor claude
New services start with the right patterns from their first commit. Engineers joining a new team install the rules and immediately get AI suggestions that match how that team builds services.
Microservices multiply the surface area for inconsistency. Every new service is an opportunity for convention drift. AI coding rules close that gap by encoding your architecture decisions where the AI can act on them: in the rules files it reads before generating a single line of code.
Create a free account on localskills.sh and publish your microservices standards as a shared skill today.
npm install -g @localskills/cli
localskills login
localskills publish