O contrato inclui o significado
Uma interface pode continuar a produzir JSON válido e deixar de servir corretamente os consumidores. Num exemplo fictício, duration mantém nome e tipo, mas passa de milissegundos para segundos. O leitor continua a dividir por mil e interpreta 1,5 segundos como 0,0015. A validação de tipos não deteta esta mudança de significado. O mesmo acontece quando um produtor acrescenta HELD a um estado textual que o consumidor só sabe tratar como READY ou DONE. Antes de recomendar a alteração, identifica unidades, valores permitidos, ausência, null, invariantes e comportamento em erro. Para valores monetários do exercício, documenta moeda e unidade mínima; não assumes que duas casas visíveis no ecrã demonstram cálculo correto. Uma revisão útil pede exemplos que contrariem as hipóteses frágeis, além do percurso que já funciona. Estes contratos são inventados para aprendizagem e não descrevem APIs de um banco.
Construir a matriz de transição
C1 lê apenas amount. C2 lê amount e amountMinor. P1 escreve apenas amount; P2 escreve apenas amountMinor. As quatro combinações não são equivalentes: C1/P2 falha. Se todos os leitores necessários puderem migrar primeiro para C2, P1 continua utilizável durante essa etapa e P2 pode ser introduzido depois. Se vinte por cento ainda forem C1, a maioria atualizada não prova compatibilidade total. O plano também precisa de analisar reversão: voltar só os leitores para C1, mantendo P2, recria a combinação incompatível. Regista a sequência, os responsáveis por cada consumidor, critérios de passagem e alternativas de recuperação. Um mock que já devolve amountMinor pode esconder que o produtor real da primeira fase só devolve amount. Liga a evidência à versão e à combinação observadas. Chamadas ausentes durante 48 horas não demonstram migração de um parceiro que só utiliza a interface no fecho trimestral.
Rever alteração parcial e repetição
A semântica depende do formato e do contrato. No JSON Merge Patch da RFC 7396, null num membro existente indica remoção; omiti-lo não pede a mesma alteração. Não generalizes essa regra para todos os formatos PATCH. Para repetição, a RFC 9110 distingue métodos idempotentes e limita a repetição automática de pedidos não idempotentes sem conhecimento adicional. Um POST cuja resposta se perdeu pode já ter produzido efeitos. No contrato fictício do laboratório, uma chave liga-se ao conteúdo original: repetir a mesma chave e quantidade devolve o resultado lógico anterior, enquanto outra quantidade produz conflito. Este modelo não define um protocolo universal nem uma implementação durável. Numa revisão real, pergunta pela identidade da operação, retenção, concorrência, efeitos externos, reconciliação e comportamento após reinício. A presença de uma chave não responde sozinha a essas perguntas.
Executar os casos locais e delimitar conclusões
Guarda o código completo como technical-contracts.py e executa python3 technical-contracts.py. Foi executado com CPython 3.13.1, sem pacotes adicionais, e produz dezassete grupos de verificações. As primeiras quatro exercem leitores e produtores fictícios, seguidas de exemplos de estado, contact, unidades, precisão e total. O helper contact_patch só suporta um campo plano com string ou null; não é uma implementação geral da RFC. read_minor só representa os dois payloads do exercício e não valida contratos monetários arbitrários. O dicionário de operações não oferece atomicidade, autenticação ou persistência. Altera o consumidor C1 para aceitar a nova representação e prevê a matriz resultante. Depois remove a asserção sobre total: explica o defeito que deixaria de ser observado. Usa os resultados para propor verificações reais, sem os apresentar como aprovação de arquitetura, ensaio de carga ou migração de produção.
"""Original DR fictional contract fixtures; no server or production validation."""
from decimal import Decimal
from math import ceil
import hashlib
import json
import platform
from pathlib import Path
def read_minor(reader, payload):
# Only the two fictional representations below are modeled.
if reader == "C2" and "amountMinor" in payload:
return payload["amountMinor"]
return int(Decimal(payload["amount"]) * 100)
def contact_patch(record, patch):
# Deliberately restricted to one flat string-or-null member, not RFC conformance.
if set(patch) - {"contact"} or any(v is not None and not isinstance(v, str) for v in patch.values()):
raise ValueError("Only a flat contact string or null is supported")
result = dict(record)
if "contact" in patch:
if patch["contact"] is None:
result.pop("contact", None)
else:
result["contact"] = patch["contact"]
return result
def operation(store, key, amount):
# Single-process teaching dictionary: no authentication, atomicity or durability.
if key in store:
return "replayed" if store[key] == amount else "conflict"
store[key] = amount
return "created"
def nearest_rank(values, percentile):
# Explicit exercise convention; not a model of Prometheus interpolation.
if not values or not 0 < percentile <= 100:
raise ValueError("Nonempty values and a percentile in (0, 100] required")
return sorted(values)[ceil(len(values) * percentile / 100) - 1]
def run():
checks = []
def check(name, actual, expected):
if actual != expected:
raise AssertionError((name, actual, expected))
checks.append({"name": name, "actual": actual, "passed": True})
producers = {"P1": {"amount": "12.34", "currency": "EUR"},
"P2": {"amountMinor": 1234, "currency": "EUR"}}
for reader in ["C1", "C2"]:
for producer, payload in producers.items():
try:
actual = read_minor(reader, payload)
except KeyError:
actual = "missing-field"
check(f"contract_{reader}_{producer}", actual,
"missing-field" if (reader, producer) == ("C1", "P2") else 1234)
check("new_state_is_not_accepted_by_old_consumer", "HELD" in {"READY", "DONE"}, False)
record = {"contact": "ops@example.test", "name": "fictional"}
check("omitted_contact_is_retained", contact_patch(record, {}), record)
check("null_contact_removes_member", contact_patch(record, {"contact": None}), {"name": "fictional"})
check("unit_change_breaks_old_conversion", str(Decimal("1.5") / 1000), "0.0015")
check("representation_affects_exact_comparison",
{"decimal": Decimal("0.10") + Decimal("0.20") == Decimal("0.30"), "float": 0.1 + 0.2 == 0.3},
{"decimal": True, "float": False})
check("numeric_types_do_not_establish_total", sum([40, 60]) == 90, False)
store = {}
check("same_key_same_content_and_changed_content",
[operation(store, "OP1", 1234), operation(store, "OP1", 1234), operation(store, "OP1", 1300)],
["created", "replayed", "conflict"])
check("sequential_path_has_ten_ms_before_overhead", 300 - sum([150, 80, 60]), 10)
check("attempts_and_waits_exceed_deadline", {"durationMs": 3 * 100 + 20 + 40, "excessMs": 3 * 100 + 20 + 40 - 300},
{"durationMs": 360, "excessMs": 60})
check("nested_total_attempts_multiply", 2 * 3, 6)
check("constant_lossless_queue_accumulates", (200 - 150) * 20, 1000)
check("tenant_identifier_is_part_of_identity",
{"idOnlyCollides": 7 == 7, "tupleCollides": ("A", 7) == ("B", 7)},
{"idOnlyCollides": True, "tupleCollides": False})
a, b = [1] * 19 + [100], [10] * 20
check("mean_of_percentiles_is_not_global_percentile",
{"aP95": nearest_rank(a, 95), "bP95": nearest_rank(b, 95),
"meanOfP95": (nearest_rank(a, 95) + nearest_rank(b, 95)) / 2,
"globalP95": nearest_rank(a + b, 95)},
{"aP95": 1, "bP95": 10, "meanOfP95": 5.5, "globalP95": 10})
return {"scope": "Fictional local representations, arithmetic and dictionary state only. No network, concurrent SDK, durable idempotency, authorization enforcement, production migration, load test or full RFC implementation is exercised. Supplied tenant names are not authenticated. Percentiles use an explicit nearest-rank convention, not Prometheus interpolation.",
"python": platform.python_version(), "scriptSha256": hashlib.sha256(Path(__file__).read_bytes()).hexdigest(),
"groups": len(checks), "checks": checks}
if __name__ == "__main__":
print(json.dumps(run(), ensure_ascii=False, indent=2))Uma resposta com lines=[40,60] e total=90 passa tipos numéricos, mas viola total=sum(lines). O teste deve observar essa relação.
Armadilhas comuns
Tipo como significado; maioria como compatibilidade; mock como prova do fornecedor; omissão igual a null; chave como garantia de exatamente um efeito.
Tópicos relacionados: Contratos de integração · Revisão de código · Idempotência e reconciliação
Revê todos os estados relevantes da transição e o significado dos valores, incluindo o caminho de recuperação.
Referência: Best practices for RESTful web API design · Google Engineering Practices, SRE and DORA; Microsoft architecture decision and collaboration guidance; OWASP threat modeling; UK lead developer framework; inspected 2026-10-01