You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/content/articles/idempotent-message-handlers-deduplication-retries/index.mdx
+18-20Lines changed: 18 additions & 20 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -31,26 +31,24 @@ There are four common strategies, each with distinct tradeoffs:
31
31
32
32
<List
33
33
variant="check-icons-list"
34
-
content={{
35
-
items: [
36
-
{
37
-
lead: "Producer-supplied message ID",
38
-
text: "is the cleanest approach. The producer generates a UUID or ULID when creating the message and includes it in the payload. This key survives redeliveries because it's part of the message content, not queue metadata. The downside is that it requires producer discipline - every producer must generate and include a unique ID.",
39
-
},
40
-
{
41
-
lead: "Content hash",
42
-
text: "works when you can't control producers. Hash the message payload with SHA-256 and use that as the key. Identical content produces identical keys automatically. The risk is that sometimes identical content _should_ be processed multiple times (two separate orders for the same product), and a content hash would incorrectly dedupe them.",
43
-
},
44
-
{
45
-
lead: "Business key composite",
46
-
text: "combines entity identifiers with operation context: `order:12345:payment:v3`. This approach has semantic meaning, making debugging easier, but requires careful thought about what constitutes a unique operation.",
47
-
},
48
-
{
49
-
lead: "Queue message ID",
50
-
text: "- avoid this approach. SQS assigns a new MessageId on each redelivery. RabbitMQ's delivery tag changes. This approach only deduplicates within a single delivery attempt, which defeats the purpose entirely.",
51
-
},
52
-
],
53
-
}}
34
+
items={[
35
+
{
36
+
lead: "Producer-supplied message ID",
37
+
text: "is the cleanest approach. The producer generates a UUID or ULID when creating the message and includes it in the payload. This key survives redeliveries because it's part of the message content, not queue metadata. The downside is that it requires producer discipline - every producer must generate and include a unique ID.",
38
+
},
39
+
{
40
+
lead: "Content hash",
41
+
text: "works when you can't control producers. Hash the message payload with SHA-256 and use that as the key. Identical content produces identical keys automatically. The risk is that sometimes identical content _should_ be processed multiple times (two separate orders for the same product), and a content hash would incorrectly dedupe them.",
42
+
},
43
+
{
44
+
lead: "Business key composite",
45
+
text: "combines entity identifiers with operation context: `order:12345:payment:v3`. This approach has semantic meaning, making debugging easier, but requires careful thought about what constitutes a unique operation.",
46
+
},
47
+
{
48
+
lead: "Queue message ID",
49
+
text: "- avoid this approach. SQS assigns a new MessageId on each redelivery. RabbitMQ's delivery tag changes. This approach only deduplicates within a single delivery attempt, which defeats the purpose entirely.",
50
+
},
51
+
]}
54
52
/>
55
53
56
54
The recommended pattern combines producer-supplied IDs with business context:
Copy file name to clipboardExpand all lines: src/content/articles/internal-cli-kubectl-terraform-wrapper-abstraction/index.mdx
+57-63Lines changed: 57 additions & 63 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -27,26 +27,24 @@ Not all wrappers are created equal. Some genuinely earn their maintenance cost;
27
27
28
28
<List
29
29
variant="check-icons-list"
30
-
content={{
31
-
items: [
32
-
{
33
-
lead: "Complexity hiding",
34
-
text: "is the strongest justification. When a workflow requires five or more manual steps with dependencies and ordering constraints, a wrapper reduces cognitive load and enforces the correct sequence. A `deploy-service` command that builds, pushes, runs helm upgrade, and executes smoke tests in the right order prevents the \"I forgot to push the image before deploying\" class of errors.",
35
-
},
36
-
{
37
-
lead: "Guard rails",
38
-
text: "prevent dangerous operations. A `kubectl-safe` wrapper that blocks `delete` in production without an approval ticket prevents the kind of outages that make the news. This works when the underlying tool allows dangerous operations with severe consequences and the policy can be codified clearly.",
39
-
},
40
-
{
41
-
lead: "Context injection",
42
-
text: "automatically selects environment-specific configuration. A terraform wrapper that auto-selects workspace, backend, and var files based on your git branch eliminates \"I thought I was in staging\" mistakes - but only if it _shows_ you what it detected. Silent context switching is how you get production outages.",
43
-
},
44
-
{
45
-
lead: "Credential management",
46
-
text: "handles authentication complexity transparently. A kubectl wrapper that auto-refreshes tokens and selects the correct cluster config reduces auth friction when tokens expire frequently and manual credential management is error-prone.",
47
-
},
48
-
],
49
-
}}
30
+
items={[
31
+
{
32
+
lead: "Complexity hiding",
33
+
text: "is the strongest justification. When a workflow requires five or more manual steps with dependencies and ordering constraints, a wrapper reduces cognitive load and enforces the correct sequence. A `deploy-service` command that builds, pushes, runs helm upgrade, and executes smoke tests in the right order prevents the \"I forgot to push the image before deploying\" class of errors.",
34
+
},
35
+
{
36
+
lead: "Guard rails",
37
+
text: "prevent dangerous operations. A `kubectl-safe` wrapper that blocks `delete` in production without an approval ticket prevents the kind of outages that make the news. This works when the underlying tool allows dangerous operations with severe consequences and the policy can be codified clearly.",
38
+
},
39
+
{
40
+
lead: "Context injection",
41
+
text: "automatically selects environment-specific configuration. A terraform wrapper that auto-selects workspace, backend, and var files based on your git branch eliminates \"I thought I was in staging\" mistakes - but only if it _shows_ you what it detected. Silent context switching is how you get production outages.",
42
+
},
43
+
{
44
+
lead: "Credential management",
45
+
text: "handles authentication complexity transparently. A kubectl wrapper that auto-refreshes tokens and selects the correct cluster config reduces auth friction when tokens expire frequently and manual credential management is error-prone.",
46
+
},
47
+
]}
50
48
/>
51
49
52
50
### What Doesn't Warrant a Wrapper
@@ -123,25 +121,23 @@ The detection hierarchy should be predictable and documented:
And when context is injected automatically, don't override explicit user flags - if someone types `--namespace=testing`, respect it even if your wrapper thinks they should be in `production`.
@@ -152,30 +148,28 @@ Wrappers often fail gradually. Usage stays high because it's in deployment scrip
152
148
153
149
<List
154
150
variant="check-icons-list"
155
-
content={{
156
-
items: [
157
-
{
158
-
lead: "Bypass rate above 20%",
159
-
text: "means developers don't trust the wrapper for real work. They're using the \"safe\" path for routine operations but dropping to raw tools when it matters.",
160
-
},
161
-
{
162
-
lead: "Support tickets mention the wrapper",
163
-
text: "more than the underlying tool. If \"tf-deploy\" appears in more tickets than \"terraform,\" your abstraction is creating problems instead of solving them.",
164
-
},
165
-
{
166
-
lead: "Developers ask \"what command is this actually running?\"",
167
-
text: "for basic operations. Transparency has failed - they can't predict what the wrapper does.",
168
-
},
169
-
{
170
-
lead: "You're more than one major version behind",
171
-
text: "the upstream tool. You can't track releases, and the gap will only widen.",
172
-
},
173
-
{
174
-
lead: "New team members learn the raw tool first",
175
-
text: "because the wrapper's behavior is too unpredictable. Your \"simplification\" made things more complex.",
176
-
},
177
-
],
178
-
}}
151
+
items={[
152
+
{
153
+
lead: "Bypass rate above 20%",
154
+
text: "means developers don't trust the wrapper for real work. They're using the \"safe\" path for routine operations but dropping to raw tools when it matters.",
155
+
},
156
+
{
157
+
lead: "Support tickets mention the wrapper",
158
+
text: "more than the underlying tool. If \"tf-deploy\" appears in more tickets than \"terraform,\" your abstraction is creating problems instead of solving them.",
159
+
},
160
+
{
161
+
lead: "Developers ask \"what command is this actually running?\"",
162
+
text: "for basic operations. Transparency has failed - they can't predict what the wrapper does.",
163
+
},
164
+
{
165
+
lead: "You're more than one major version behind",
166
+
text: "the upstream tool. You can't track releases, and the gap will only widen.",
167
+
},
168
+
{
169
+
lead: "New team members learn the raw tool first",
170
+
text: "because the wrapper's behavior is too unpredictable. Your \"simplification\" made things more complex.",
0 commit comments