<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="rss.xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Derrops Blog</title>
        <link>https://blog.derrops.com/blog</link>
        <description>Derrops Blog</description>
        <lastBuildDate>Thu, 26 Feb 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[Derrops Guide to Naming Conventions and Segregation]]></title>
            <link>https://blog.derrops.com/blog/derrops-conventions</link>
            <guid>https://blog.derrops.com/blog/derrops-conventions</guid>
            <pubDate>Thu, 26 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[I've been meaning to make this guide, having worked at a SAAS from the very get-go, I've now got to live with some good and some bad decisions. It's been very hard to pinpoint why the naming conventions have lead to complexity. And overtime I realized that with engineering, you often make decisions based on the tradeoffs, but ultimately you do optimize for something. If you don't figure out what that something should be, you end having a solution which is not optimized for the problem you are trying to solve.]]></description>
            <content:encoded><![CDATA[<p>I've been meaning to make this guide, having worked at a SAAS from the very get-go, I've now got to live with some good and some bad decisions. It's been very hard to pinpoint why the naming conventions have lead to complexity. And overtime I realized that with engineering, you often make decisions based on the tradeoffs, but ultimately you do optimize for something. If you don't figure out what that something should be, you end having a solution which is not optimized for the problem you are trying to solve.</p>
<p>Living with a SAAS for many years now start to finish, what I ended up finding that these conventions are more important than I initially thought, and have ramifications in many different areas:</p>
<table><thead><tr><th>Area</th><th>Description</th><th>Justification</th></tr></thead><tbody><tr><td><em>Cost</em></td><td>Ability to optimize, track, and control spend across environments, services, or resources.</td><td>Consistent naming and segregation make it easier to accurately allocate and monitor resource usage by environment, service, or team.</td></tr><tr><td><em>Security</em></td><td>How easily you can enforce and audit security boundaries and practices.</td><td>Segregation and clear naming provide obvious boundaries for applying Least Privilege, monitoring access, and investigating incidents.</td></tr><tr><td><em>Complexity</em></td><td>The overhead and cognitive load of understanding and managing environments and resources.</td><td>Predictable conventions reduce ambiguity and make it simpler for everyone to locate, manage, and reason about resources.</td></tr><tr><td><em>Maintainability</em></td><td>How straightforward it is to update, refactor, or scale your environments and naming conventions.</td><td>Uniform conventions mean changes can be made systematically, minimizing the risk of errors and excessive rework.</td></tr><tr><td><em>Scalability</em></td><td>Ability to grow the number of services, accounts, or environments without hitting convention or collision issues.</td><td>Good conventions prevent collisions and manual workarounds as your organization and environments scale.</td></tr><tr><td><em>Usability</em></td><td>How easy naming and segregation is for engineers and consumers to work with daily.</td><td>Easy-to-understand resource names speed up onboarding and reduce misconfiguration by new and experienced engineers alike.</td></tr><tr><td><em>Performance</em></td><td>Influence of structure/naming on ability to optimize for latency, throughput, or efficiency.</td><td>Logical segregation enables more effective resource grouping, which can positively impact locality, caching, or policy-based optimizations.</td></tr><tr><td><em>Reliability</em></td><td>Impact on operational stability, incident response, and minimizing blast radius of issues.</td><td>Separation by environment/service/domain helps contain failures and allows more targeted incident response.</td></tr><tr><td><em>Availability</em></td><td>Affect on uptime or redundancy due to naming/segregation conventions.</td><td>Clear segregation facilitates independent deployments and failover, increasing uptime and resilience.</td></tr><tr><td><em>Compliance</em></td><td>Helps enforce regulatory, audit, and governance requirements more easily.</td><td>Accurate, well-segregated naming enables easier reporting, automated policy enforcement, and audit compliance.</td></tr></tbody></table>
<p>These areas can all be condensed into the following guiding principals</p>
<h1>Guiding Principles</h1>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-naming-consistency-principle">1. Naming Consistency Principle<a href="https://blog.derrops.com/blog/derrops-conventions#1-naming-consistency-principle" class="hash-link" aria-label="Direct link to 1. Naming Consistency Principle" title="Direct link to 1. Naming Consistency Principle" translate="no">​</a></h2>
<p><strong>Definition:</strong><br>
<!-- -->A given resource should have the <em>same name</em> in every environment—whether it’s <code>dev</code>, <code>prod</code>, or any other. For example, a resource called <code>user-service</code> should be named <code>user-service</code> in all environments, rather than <code>user-service-dev</code> or <code>user-service-prod</code>. Environment must be represented by namespace isolation, not naming convention.</p>
<p><strong>Why it matters:</strong><br>
<!-- -->Consistent resource names dramatically simplify the process of grouping, querying, and managing deployment instances across various tools and platforms. It helps reduce cognitive load and operational complexity, especially as your infrastructure grows.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>Globally unique name, such as with an s3 bucket, it can be that another customer on AWS takes the name of an S3 bucket that you had planned. One solution to this is including a random id within the name. But in general I've found this scenario rare, and losing predictability in the name which can lead to negative outcomes</p></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>Global services such as IAM which are global also may cause conflicts if you were to have more than 1 environment in an account <strong>Prefer Account per Environment if Possible</strong></p></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-naming-stability-principle">2. Naming Stability Principle<a href="https://blog.derrops.com/blog/derrops-conventions#2-naming-stability-principle" class="hash-link" aria-label="Direct link to 2. Naming Stability Principle" title="Direct link to 2. Naming Stability Principle" translate="no">​</a></h2>
<p><strong>Definition:</strong><br>
<!-- -->Prioritize naming conventions that minimize how often resource names need to change over the lifetime of your systems. Choose prefixes or structures for your names that are unlikely to require revision as your organization and requirements evolve.</p>
<p><strong>Why it matters:</strong><br>
<!-- -->Stable naming reduces refactoring, lowers risk of errors, and leads to smoother automation and scaling. The fewer changes needed, the more reliable and maintainable your environment will be in the long run.
You'll also find that following this principal results in a better security posture, more on that later.</p>
<h1>Segregation Strategy</h1>
<p>Segregation needs to be done in a strategic way. Resources segregated differently can lead to drastically different outcomes. Following a consistent pattern for <code>Segregation and Naming Conventions</code> can lead ot many different benefits:</p>
<ul>
<li class="">To avoid naming collisions</li>
<li class="">To reduce complexity by not adding unnecessary naming conventions to resources.</li>
<li class="">To make it easy to group resources across environments together. This is useful in filtering duplicates for security findings, as having different names for the same resources adds complexity to the queries needed</li>
<li class="">Easier to automate as resources are easier to target across different environments as they are named in a predictable way.</li>
<li class="">Within an environment, segregating services and organizations sharing an environment increases your security posture, as the prefixes can be used to scope access to resources and create access boundaries</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="account-segregation">Account Segregation<a href="https://blog.derrops.com/blog/derrops-conventions#account-segregation" class="hash-link" aria-label="Direct link to Account Segregation" title="Direct link to Account Segregation" translate="no">​</a></h2>
<p>How you segregate your account will determine your name space, and therefore will affect your naming conventions. When creating accounts, modern patters would be to have 1 per region and environment, but there may be cases where you take a different approach again because of other factors related to: (*Cost&amp;, <em>Compliance</em>, <em>Security</em>, etc).
But do challenge this decision if you do not segregate, as there is now especially strong multi-account support in Cloud Providers such as AWS.</p>
<blockquote>
<p>In general the more you segregate, the less likely you'll have naming collisions.</p>
</blockquote>
<table><thead><tr><th>Segment</th><th>Name</th><th>Description</th></tr></thead><tbody><tr><td>Region</td><td><code>{region}</code></td><td>If you don't segregate your Account by region, you will need to have <code>{region}</code> in the name of the resource to avoid collisions.</td></tr><tr><td>Environment</td><td><code>{env}</code></td><td>If you don't segregate your Account by environment, you will need to have <code>{env}</code> in the name of the resource to avoid collisions.</td></tr></tbody></table>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</div><div class="admonitionContent_BuS1"><p>Operationally if <code>Deployment Instances</code> have different names, it can create severe complexity when trying to groups those instances together in different DevOps tools and products. I've experienced this first hand when sifting through findings in AWS Security Hub. Just when you find a way within a given tool to group these resources and solve this, as your organization grows and adopts new tools you find yourself being faced with this issue again and again for every tool you use.</p></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>Consider comparing 2 x Lambda functions <code>foobar</code>, in 2 different environments <code>dev</code> and <code>prod</code>.
If the Deployment Instances have different names, your query will need to have an OR clause to include both, <code>foobar-dev</code> or <code>foobar-prod</code> and then also have the group by environment anyway. Whereas if they have the same name, there is no need for a clause on the name. Excluding environment reduces the mental load in many different scenarios.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="prefixing">Prefixing<a href="https://blog.derrops.com/blog/derrops-conventions#prefixing" class="hash-link" aria-label="Direct link to Prefixing" title="Direct link to Prefixing" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="segregation-hierarchy">Segregation Hierarchy<a href="https://blog.derrops.com/blog/derrops-conventions#segregation-hierarchy" class="hash-link" aria-label="Direct link to Segregation Hierarchy" title="Direct link to Segregation Hierarchy" translate="no">​</a></h3>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="prefix-structure">Prefix Structure<a href="https://blog.derrops.com/blog/derrops-conventions#prefix-structure" class="hash-link" aria-label="Direct link to Prefix Structure" title="Direct link to Prefix Structure" translate="no">​</a></h3>
<p>Structuring your names correctly is key to the success of your naming conventions.</p>
<p>If your config store is <strong>NOT</strong> located in the same namespace as the resource, you will have to have <code>{env}</code> in your naming conventions. It's challenging where to put <code>{env}</code>, as there are tradeoffs
Also note if your config store is <strong>NOT</strong> in the same <code>{region}</code>, then you will need to have <code>{region}</code> in your naming conventions.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="segment-definitions">Segment Definitions<a href="https://blog.derrops.com/blog/derrops-conventions#segment-definitions" class="hash-link" aria-label="Direct link to Segment Definitions" title="Direct link to Segment Definitions" translate="no">​</a></h2>
<table><thead><tr><th>Segment</th><th>Example</th><th>Description</th></tr></thead><tbody><tr><td><code>{region}</code></td><td>ap-southeast-2</td><td><em>(Optional if platform will be multi-region)</em> The region of the deployment. This may be required if you are not going to segregate your regions by accounts, then there will be conflicts as some services are global (AWS IAM) or share the same namespace (such as S3 Buckets which must be unique across all regions). Deciding to have different accounts for different regions mitigates this issue, but this decision can sometimes be taking other factors into account than this guide. Note this is the Datacenter Region (e.g. ap-southeast-2), not the country itself.</td></tr><tr><td><code>{env}</code></td><td>dev</td><td><em>(Only if Required)</em> The deployment environment. Only required in the prefix, when the config key lives in a different namespace than the resource accessing it. If your config is stored in the same account or namespace as the resource, this segment is redundant and should be omitted. However if a globally unique name is required, such as with an s3 bucket, this this will be required in the prefix.</td></tr><tr><td><code>{org}</code></td><td>acme</td><td>The top-level tenant or business unit. Provides hard namespace isolation. Chosen to be stable and long-lived—changes are rare and treated as a migration event.</td></tr><tr><td><code>{domain}</code></td><td>payments</td><td>A bounded business or technical capability that can be owned and reasoned about independently. More stable than a team name, more meaningful than a platform label.</td></tr><tr><td><code>{service}</code></td><td>checkout-api</td><td>The concrete, deployable unit within a domain. Specific enough to be unambiguous, broad enough to own multiple configuration values beneath it.</td></tr><tr><td><code>{partition}</code></td><td>2024/01/15/14</td><td><em>(Optional — data partitioning only)</em> A runtime-determined subdivision used to group high-volume data into discrete, filterable sets. Most common in data storage contexts such as S3 log or event data. <code>{partition}</code> is the <strong>only</strong> segment permitted to contain internal hierarchical delimiters — a single partition can represent multiple levels of granularity (e.g. <code>{year}/{month}/{day}/{hour}</code>), each of which remains a valid, queryable prefix boundary. All other segments must be flat and contain no internal delimiters. Not applicable to config stores or resource naming.</td></tr><tr><td><code>{key}</code></td><td>stripe-webhook-secret</td><td>The final, addressable artifact. Everything above it is context and namespace — <code>{key}</code> is the specific thing being referenced. The form varies by system: a config parameter name (<code>stripe-webhook-secret</code>), a filename (<code>transactions.json</code>), or an image tag (<code>1.2.3</code>) — but the concept is always the same: it uniquely identifies the artifact within its namespace. A new segment is only warranted when it creates a meaningful operational boundary — a prefix you would independently query. System-generated uniqueness suffixes (e.g. sequence numbers appended by parallel writers like Kinesis Firehose or Spark) do not qualify; they carry no business meaning and are simply part of <code>{key}</code>, but do not make up part of the identity.</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="segment-order">Segment Order<a href="https://blog.derrops.com/blog/derrops-conventions#segment-order" class="hash-link" aria-label="Direct link to Segment Order" title="Direct link to Segment Order" translate="no">​</a></h2>
<p>It is sometimes said:</p>
<blockquote>
<p>Good architecture makes change easy. Bad architecture makes change hard.</p>
</blockquote>
<p>Therefore in this decision we should optimize for making <code>Change Easy</code>, which would dictate the</p>
<blockquote>
<p>stability should decrease left to right in a prefix</p>
</blockquote>
<ul>
<li class="">Changing leftmost segments causes the greatest disruption.</li>
<li class="">Renaming service only affects its subtree.</li>
</ul>
<p>Therefore it makes sense the order of the segments should be the most stable to the least stable:</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="segment-stability">Segment Stability<a href="https://blog.derrops.com/blog/derrops-conventions#segment-stability" class="hash-link" aria-label="Direct link to Segment Stability" title="Direct link to Segment Stability" translate="no">​</a></h2>
<table><thead><tr><th>Segment</th><th>Question</th><th>Stability</th><th>Scope Boundary</th><th>Stability</th></tr></thead><tbody><tr><td><code>{region}</code></td><td><strong>Where</strong> in the world?</td><td>Extremely High</td><td>Infrastructure locality boundary</td><td>for all intensive purposes will never change</td></tr><tr><td><code>{env}</code></td><td><strong>Which</strong> deployment stage?</td><td>Extremely High</td><td>Deployment lifecycle boundary</td><td>for all intensive purposes will never change</td></tr><tr><td><code>{org}</code></td><td><strong>Who</strong> owns the resource?</td><td>Very High</td><td>Ownership boundary</td><td>changes almost never, unless re-org</td></tr><tr><td><code>{domain}</code></td><td><strong>What</strong> business capability?</td><td>High</td><td>Capability boundary</td><td>changes rarely, capabilities outlive teams, only if domain is modelled differently and refactored</td></tr><tr><td><code>{service}</code></td><td><strong>Which</strong> deployable unit?</td><td>Medium</td><td>Deployment unit boundary</td><td>changes occasionally, deployable units get renamed, split or merged</td></tr><tr><td><code>{partition}</code></td><td><strong>How</strong> is data subdivided?</td><td>Very Low <em>(Optional)</em></td><td>Data partitioning boundary</td><td>changes constantly; new values generated at runtime (e.g. a new date partition every day). Only relevant for data storage — omit everywhere else</td></tr><tr><td><code>{key}</code></td><td><strong>What</strong> configuration value?</td><td>Low</td><td>Configuration/value boundary</td><td>changes most frequently</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="fully-qualified-examples">Fully Qualified Examples<a href="https://blog.derrops.com/blog/derrops-conventions#fully-qualified-examples" class="hash-link" aria-label="Direct link to Fully Qualified Examples" title="Direct link to Fully Qualified Examples" translate="no">​</a></h3>
<table><thead><tr><th>Example</th><th>Value</th></tr></thead><tbody><tr><td>Hierarchical (config stores)</td><td><code>/ap-southeast-2/prod/acme/payments/checkout-api/stripe-webhook-secret</code></td></tr><tr><td>Compound kebab-case (resource names)</td><td><code>/ap-southeast-2/prod/acme/payments/checkout-api/stripe-webhook-secret</code></td></tr><tr><td>Account-segregated (preferred):</td><td><code>acme--payments--checkout-api--stripe-webhook-secret</code></td></tr><tr><td>Data storage with partition (S3 object key)</td><td><code>acme/payments/checkout-api/2024-01-15/transactions.json</code></td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="delimiters">Delimiters<a href="https://blog.derrops.com/blog/derrops-conventions#delimiters" class="hash-link" aria-label="Direct link to Delimiters" title="Direct link to Delimiters" translate="no">​</a></h3>
<p>Now that we have segments, and their order, we need to decide on the delimiters to use.
Here are some possible candidates evaluated over different resource types.
You'll see that the <code>-</code> is most supported. <code>_</code> is a strong candidate but fails when it comes to host names and S3 bucket names.</p>
<p><strong>Possible Candidates for Delimiters:</strong></p>
<table><thead><tr><th>Delimiter</th><th>Hostname</th><th>AWS Stack Name (CloudFormation)</th><th>URL Path</th><th>S3 Bucket Name</th><th>Safe Across ALL?</th><th>Notes</th></tr></thead><tbody><tr><td><code>-</code> hyphen</td><td>✅ Yes</td><td>✅ Yes</td><td>✅ Yes</td><td>✅ Yes</td><td>⭐ Yes (BEST)</td><td>Universal standard. Recommended everywhere.</td></tr><tr><td><code>_</code> underscore</td><td>❌ No</td><td>✅ Yes</td><td>✅ Yes</td><td>❌ No</td><td>❌ No</td><td>Breaks hostname and S3 bucket compatibility</td></tr><tr><td><code>.</code> dot</td><td>✅ Yes</td><td>✅ Yes</td><td>✅ Yes</td><td>✅ Yes (with caveats)</td><td>⚠️ Conditional</td><td>Breaks TLS wildcard matching, avoid in bucket names</td></tr><tr><td><code>/</code> slash</td><td>❌ No</td><td>❌ No</td><td>✅ Yes</td><td>❌ No (delimiter only in key)</td><td>❌ No</td><td>Only valid as URL path separator</td></tr><tr><td>space</td><td>❌ No</td><td>❌ No</td><td>❌ Encoded</td><td>❌ No</td><td>❌ No</td><td>Never use</td></tr><tr><td><code>--</code> double hyphen</td><td>✅ Yes</td><td>✅ Yes</td><td>✅ Yes</td><td>✅ Yes</td><td>⭐ Yes</td><td>Common and safe variant</td></tr></tbody></table>
<p>There are different types of delimiters. Sometimes we are representing differences between the different segments of the prefix, other times we are representing differences between the different words with the same segment.
Therefore we need a delimiter for each case.
As <code>-</code> is the only supported delimiter for all resource types, including another is not ideal because it will reduce the resources types which can follow that convention. Instead the <code>--</code> is used to delimit between the different segments of the prefix, and a <code>-</code> is used to delimit between the different words with the same segment.</p>
<table><thead><tr><th>Naming Convention</th><th>Priority</th><th>Resource Type</th><th>Delimiter</th><th>Word Delimiter</th><th>Description</th><th>Example</th></tr></thead><tbody><tr><td><em>hierarchical-case</em></td><td>1</td><td>Config Keys</td><td><code>/</code></td><td><code>-</code></td><td>Hierarchical naming convention using a path-style namespace, useful for configuration keys.</td><td><code>/acme/store/checkout/api-key</code></td></tr><tr><td><em>compound kebab-case</em></td><td>2</td><td>Resource IDs</td><td><code>--</code></td><td><code>-</code></td><td>Kebab-case is URL and code friendly; more acceptable than underscores for most services, especially for resource IDs.</td><td><code>acme--store--checkout--api-feature</code></td></tr></tbody></table>
<ul>
<li class="">Often <code>1</code> is not possible as a name (for example IAC solutions such as AWS Cloudformation)</li>
<li class=""><code>-</code> is preferred over <code>_</code> as it is more URL friendly (can appear in the host name where as underscore cannot).</li>
<li class=""><code>--</code> whilst this doesn't look the most pleasing to the eye, it is more URL friendly and distinguishes between the hierarchial delimiters and word delimiters.</li>
<li class="">other tools will not use <code>--</code> as a delimiter, so using it as a delimiter in the prefix for a key will not be compatible with other tools.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="native-delimiters">Native Delimiters<a href="https://blog.derrops.com/blog/derrops-conventions#native-delimiters" class="hash-link" aria-label="Direct link to Native Delimiters" title="Direct link to Native Delimiters" translate="no">​</a></h3>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>If a resource already has native support for a delimiter, it should be used instead of the <code>--</code> delimiter, such as <code>/</code> in AWS S3, for storing objects. Otherwise you would lose functionality such as using the <code>prefix</code> to filter objects in a bucket.</p></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>An exception to this rule might be when you plan on centralizing data collection, and you want all data to be stored in a single location. You may want to preemptively segregate the data in each environment already, even though there will be only 1 segment, to simplify the sync operation.</p></div></div>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>We will assume going forward there is only 1 region, so there is no need for segregation by region, but if there wasn't, region would need to be included in the prefix</p></div></div>
<p><strong>Full example:</strong></p>
<ol>
<li class=""><code>/{org}/{domain}/{service}/{key}</code> =&gt; <code>/acme/payments/checkout-api/stripe-webhook-secret</code></li>
<li class=""><code>{org}--{domain}--{service}--{key}</code> =&gt; <code>acme--payments--checkout-api--stripe-webhook-secret</code></li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="rational-for-convention">Rational for Convention<a href="https://blog.derrops.com/blog/derrops-conventions#rational-for-convention" class="hash-link" aria-label="Direct link to Rational for Convention" title="Direct link to Rational for Convention" translate="no">​</a></h2>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="every-prefix-is-a-meaningful-operational-boundary">Every prefix is a meaningful operational boundary<a href="https://blog.derrops.com/blog/derrops-conventions#every-prefix-is-a-meaningful-operational-boundary" class="hash-link" aria-label="Direct link to Every prefix is a meaningful operational boundary" title="Direct link to Every prefix is a meaningful operational boundary" translate="no">​</a></h4>
<p><strong>Account-segregated (preferred) — config store:</strong></p>
<table><thead><tr><th>Prefix</th><th>Boundary</th><th>Example Operation</th></tr></thead><tbody><tr><td><code>/acme/*</code></td><td>Entire org</td><td>Audit all config across the org</td></tr><tr><td><code>/acme/payments/*</code></td><td>Domain</td><td>Grant the payments team read access to their config</td></tr><tr><td><code>/acme/payments/checkout-api/*</code></td><td>Service</td><td>Fetch all config for a service on startup</td></tr><tr><td><code>/acme/payments/checkout-api/stripe-webhook-secret</code></td><td>Value</td><td>Read a specific secret</td></tr></tbody></table>
<p><strong>Account-segregated — data storage with partition (e.g. S3 log data):</strong></p>
<table><thead><tr><th>Prefix</th><th>Boundary</th><th>Example Operation</th></tr></thead><tbody><tr><td><code>acme/</code></td><td>Entire org</td><td>Replicate all org data to a central bucket</td></tr><tr><td><code>acme/payments/</code></td><td>Domain</td><td>Process all events owned by payments</td></tr><tr><td><code>acme/payments/checkout-api/</code></td><td>Service</td><td>Backfill or reprocess all logs for a service</td></tr><tr><td><code>acme/payments/checkout-api/2024/</code></td><td>Partition — year</td><td>Archive or aggregate a full year of data</td></tr><tr><td><code>acme/payments/checkout-api/2024/01/</code></td><td>Partition — month</td><td>Load a month's data into a warehouse</td></tr><tr><td><code>acme/payments/checkout-api/2024/01/15/</code></td><td>Partition — day</td><td>Replay a day's events or run a daily job</td></tr><tr><td><code>acme/payments/checkout-api/2024/01/15/14/</code></td><td>Partition — hour</td><td>Download all log data for a specific hour</td></tr><tr><td><code>acme/payments/checkout-api/2024/01/15/14/transactions-00001.json</code></td><td>Value</td><td>Fetch a specific log file</td></tr></tbody></table>
<p><strong>Non-account-segregated — env and region included:</strong></p>
<table><thead><tr><th>Prefix</th><th>Boundary</th><th>Example Operation</th></tr></thead><tbody><tr><td><code>/ap-southeast-2/*</code></td><td>Region</td><td>List all resources in a region</td></tr><tr><td><code>/ap-southeast-2/prod/*</code></td><td>Environment</td><td>Audit all production config</td></tr><tr><td><code>/ap-southeast-2/prod/acme/*</code></td><td>Org</td><td>Fetch all org config in that env</td></tr><tr><td><code>/ap-southeast-2/prod/acme/payments/*</code></td><td>Domain</td><td>Grant payments access to their prod config</td></tr><tr><td><code>/ap-southeast-2/prod/acme/payments/checkout-api/*</code></td><td>Service</td><td>Fetch all config for a service on startup</td></tr><tr><td><code>/ap-southeast-2/prod/acme/payments/checkout-api/stripe-webhook-secret</code></td><td>Value</td><td>Read a specific secret</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="segment-definitions-1">Segment Definitions<a href="https://blog.derrops.com/blog/derrops-conventions#segment-definitions-1" class="hash-link" aria-label="Direct link to Segment Definitions" title="Direct link to Segment Definitions" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="region">Region<a href="https://blog.derrops.com/blog/derrops-conventions#region" class="hash-link" aria-label="Direct link to Region" title="Direct link to Region" translate="no">​</a></h3>
<p>The region identifies the physical or logical infrastructure region where the deployment instance resides.</p>
<p>Examples:</p>
<ul>
<li class="">ap-southeast-2</li>
<li class="">us-east-1</li>
<li class="">eu-west-1</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="org">Org<a href="https://blog.derrops.com/blog/derrops-conventions#org" class="hash-link" aria-label="Direct link to Org" title="Direct link to Org" translate="no">​</a></h3>
<p>The org identifies the top-level organizational boundary. Which department owns the resource.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>Not the team itself as teams can change more frequently than the org itself, otherwise this becomes too unstable to use as a namespace boundary.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="domain">Domain<a href="https://blog.derrops.com/blog/derrops-conventions#domain" class="hash-link" aria-label="Direct link to Domain" title="Direct link to Domain" translate="no">​</a></h3>
<ul>
<li class="">Encodes business capability rather than org structure — capabilities are far more stable than teams or products over time</li>
<li class="">Well understood in modern engineering culture thanks to DDD, Team Topologies, and microservices discourse</li>
<li class="">Naturally guides correct usage — engineers intuitively know whether something belongs to payments or identity</li>
<li class="">Aligns with how most mature orgs already think about their architecture, even if they don't use the word</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="service">Service<a href="https://blog.derrops.com/blog/derrops-conventions#service" class="hash-link" aria-label="Direct link to Service" title="Direct link to Service" translate="no">​</a></h3>
<p>The service represents the concrete deployable unit. This is the actual runtime component which is typically the primary identity engineers interact with..</p>
<p>Examples:</p>
<ul>
<li class="">checkout-api</li>
<li class="">auth-service</li>
<li class="">webhook-worker</li>
<li class="">billing-scheduler</li>
</ul>
<p>Deployment Units could be in the form of:</p>
<ul>
<li class="">Lambda function</li>
<li class="">Microservice</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="key">Key<a href="https://blog.derrops.com/blog/derrops-conventions#key" class="hash-link" aria-label="Direct link to Key" title="Direct link to Key" translate="no">​</a></h2>
<p>The key identifies a specific configuration value, secret, resource, or parameter belonging to a service.
Represents the exact value being referenced.
Everything to the left provides context.</p>
<ul>
<li class="">Created</li>
<li class="">Deleted</li>
<li class="">Renamed</li>
<li class="">Rotated</li>
</ul>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>Optional: This principal, when dogmatically applied may mean that you segregate data in an environment, even when there will only be 1 segment, to simplify the sync operation to a central location later.
As the end destination will need to be segmented, but the source data not necessarily.
But by segregating the data in the source we achieve this principle.</p><p>This contradicts another principle in guiding how to segregate data, which would say if you only were going to have 1 segment, you should not segregate the data in the first place. But if you can see the future, and you know that you are going to combine data together, then you are best off already pre-paring for this scenario so it doesn't hit you later.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="tagging-suggestions">Tagging (Suggestions)<a href="https://blog.derrops.com/blog/derrops-conventions#tagging-suggestions" class="hash-link" aria-label="Direct link to Tagging (Suggestions)" title="Direct link to Tagging (Suggestions)" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="tagging-env">Tagging Env<a href="https://blog.derrops.com/blog/derrops-conventions#tagging-env" class="hash-link" aria-label="Direct link to Tagging Env" title="Direct link to Tagging Env" translate="no">​</a></h3>
<p>Many would argue that you need to tag every resource with what environment it lives in. I would argue that if you are segregating environments by accounts, then tags are redundant. Tagging resources would then violate the DRY Do Not Repeat Yourself principle, and add to overhead in needing more unnecessary tagging policies. Whilst it's fairly easy to tag resources you manage, sometimes tools or other entities manage resources in your account and it can be problematic to tag them.</p>
<p><code>Account</code> itself is a stronger way (than tagging) to group resources across an environment together, as an <code>Account</code> is an actual <strong>container</strong> for resources, and not just a <strong>label</strong>. It's easier to make mistakes and not tag a resource for some amount of time, cause cost allocation calculations to be incorrect, whereas usage by <code>Account</code> is always accurate.</p>
<p>If there is some other need to tag resources, such as for security or compliance or because of a tool or service you are using.
Then have the <code>Account</code> the source of truth and perform batch tagging as needed. This will reduce the <em>Maintainability</em>.</p>
<h1>Concrete Examples:</h1>
<p>For the naming convention we don't want to have the <code>{env}</code> and <code>{region}</code> because of the <code>Naming Consistency Principle</code>.
But because of uniqueness constraints within a namespace this is not always possible. See uniqueness constraints below:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="uniqueness-constraints">Uniqueness Constraints<a href="https://blog.derrops.com/blog/derrops-conventions#uniqueness-constraints" class="hash-link" aria-label="Direct link to Uniqueness Constraints" title="Direct link to Uniqueness Constraints" translate="no">​</a></h3>
<table><thead><tr><th>Service</th><th>Scope Boundary</th><th>region</th><th>env</th><th>Example</th></tr></thead><tbody><tr><td>Global Namespace</td><td>Globally Unique across all Namespaces</td><td>✅</td><td>✅</td><td>AWS S3 Bucket</td></tr><tr><td>Global Service</td><td>Unique in all Regions within a Namespace</td><td>✅</td><td>❌</td><td>IAM Role Name</td></tr><tr><td>Regional Namespace</td><td>Unique in a Region within a Scope Boundary</td><td>❌</td><td>❌</td><td>RDS Instance Identifier</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="concrete-example">Concrete Example<a href="https://blog.derrops.com/blog/derrops-conventions#concrete-example" class="hash-link" aria-label="Direct link to Concrete Example" title="Direct link to Concrete Example" translate="no">​</a></h3>
<table><thead><tr><th>Resource Type</th><th>Scope</th><th>Global?</th><th>Physical Name</th><th><code>{region}</code> required</th><th><code>{env}</code> required</th><th>Rationale</th></tr></thead><tbody><tr><td>S3 Bucket</td><td>Global</td><td>✅</td><td>ap-southeast-2--prod--acme--payments--checkout-api--backup-storage</td><td>✅</td><td>✅</td><td>Must be globally unique</td></tr><tr><td>Route53 Hosted Zone</td><td>Global</td><td>✅</td><td>prod.acme.com</td><td>❌</td><td>✅</td><td>Each env account is delegated its own subdomain of the apex domain</td></tr><tr><td>Route53 Record</td><td>Zone scope</td><td>✅</td><td>checkout-api.prod.acme.com</td><td>❌</td><td>✅</td><td>DNS global namespace</td></tr><tr><td>CloudFront Distribution Alias</td><td>Global</td><td>✅</td><td>checkout-api.prod.acme.com</td><td>❌</td><td>✅</td><td>Global DNS namespace</td></tr><tr><td>ACM Certificate Domain</td><td>Global</td><td>✅</td><td>checkout-api.prod.acme.com</td><td>❌</td><td>✅</td><td>Global DNS namespace</td></tr><tr><td>S3 Static Website</td><td>Global</td><td>✅</td><td>prod.acme-checkout-api</td><td>✅</td><td>✅</td><td>Global namespace</td></tr></tbody></table>
<h1>Following Native Hierarchies</h1>
<p>Many systems have their own built-in hierarchical constructs — path delimiters, subdomain delegation, namespace scoping. When a system already provides a hierarchy, map your naming segments onto it rather than encoding structure into flat compound names.</p>
<blockquote>
<p><strong>If the system provides a hierarchy, use it. Don't fight it.</strong></p>
</blockquote>
<p>The same logical segments (<code>{org}</code>, <code>{env}</code>, <code>{domain}</code>, <code>{service}</code>, <code>{key}</code>) apply regardless of the system — only the direction, delimiter, and order may differ. Mapping onto native hierarchies preserves the system's built-in ability to filter, delegate, and scope by prefix or level.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-use-native-hierarchies">Why Use Native Hierarchies?<a href="https://blog.derrops.com/blog/derrops-conventions#why-use-native-hierarchies" class="hash-link" aria-label="Direct link to Why Use Native Hierarchies?" title="Direct link to Why Use Native Hierarchies?" translate="no">​</a></h2>
<p>Native hierarchies provide critical operational benefits that flat compound names cannot:</p>
<ol>
<li class=""><strong>Prefix Filtering &amp; Querying</strong> - Systems like S3, SSM, and CloudWatch Logs support prefix-based queries, enabling you to fetch all resources for an org, domain, or service in a single operation</li>
<li class=""><strong>Permission Scoping</strong> - IAM and other RBAC systems can grant access via path prefixes, enabling least-privilege access without listing every resource</li>
<li class=""><strong>Console Organization</strong> - AWS console and CLI tools can drill-down and organize resources hierarchically, improving usability</li>
<li class=""><strong>Operational Boundaries</strong> - Each prefix level becomes a meaningful operational boundary for automation, monitoring, and access control</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="priority-decision-native-hierarchy-vs-compound-naming">Priority Decision: Native Hierarchy vs. Compound Naming<a href="https://blog.derrops.com/blog/derrops-conventions#priority-decision-native-hierarchy-vs-compound-naming" class="hash-link" aria-label="Direct link to Priority Decision: Native Hierarchy vs. Compound Naming" title="Direct link to Priority Decision: Native Hierarchy vs. Compound Naming" translate="no">​</a></h2>
<p><strong>When naming a resource, follow this priority:</strong></p>
<ol>
<li class=""><strong>Does the service support native hierarchy?</strong> → Use the native hierarchy with its delimiter (<code>/</code>, <code>.</code>, <code>:</code>, etc.)</li>
<li class=""><strong>No native hierarchy?</strong> → Use compound kebab-case with <code>--</code> segment delimiters and <code>-</code> word delimiters</li>
</ol>
<p><strong>Example - CloudWatch Metrics (Critical Pattern):</strong></p>
<p>CloudWatch Metrics have THREE naming components, each serving a distinct purpose:</p>
<table><thead><tr><th>Component</th><th>What Goes Here</th><th>Delimiter</th><th>Example</th><th>Purpose</th></tr></thead><tbody><tr><td><strong>Namespace</strong></td><td><code>{org}/{domain}</code> ONLY</td><td><code>/</code></td><td><code>acme/payments</code></td><td>Organize metrics by business capability; enables filtering by domain</td></tr><tr><td><strong>Dimensions</strong></td><td><code>service={service}</code> + operational metadata</td><td>Key-value pairs</td><td><code>service=checkout-api</code> (env via account, NOT dimension)</td><td>Enable cross-service queries; query "all services in payments with high CPU"</td></tr><tr><td><strong>Metric Name</strong></td><td>Specific metric being measured</td><td><code>-</code> for words</td><td><code>request-count</code>, <code>error-rate</code>, <code>latency-p99</code></td><td>Identify what is being measured</td></tr></tbody></table>
<p>❌ WRONG: Encoding service in namespace → Namespace: <code>acme/payments/checkout-api</code>, Metric: <code>request-count</code></p>
<ul>
<li class="">Problem: Cannot query across services ("show me high CPU for ALL services in payments")</li>
<li class="">You'd need separate queries for each service</li>
</ul>
<p>✅ RIGHT: Service as a dimension → Namespace: <code>acme/payments</code>, Dimension: <code>service=checkout-api</code>, Metric: <code>request-count</code></p>
<ul>
<li class=""><strong>Env handling (account-segregated, RECOMMENDED):</strong> Each account's metrics are naturally isolated; no <code>env</code> dimension needed<!-- -->
<ul>
<li class="">Prod account metrics: <code>acme/payments</code> namespace, <code>service=checkout-api</code> dimension</li>
<li class="">Dev account metrics: <code>acme/payments</code> namespace, <code>service=checkout-api</code> dimension</li>
<li class="">Permission boundary = AWS account; queries automatically scoped by account access</li>
</ul>
</li>
<li class=""><strong>Env handling (if NOT account-segregated):</strong> Add <code>env</code> dimension for isolation<!-- -->
<ul>
<li class="">Both environments in same account: <code>service=checkout-api,env=prod</code> vs <code>service=checkout-api,env=dev</code></li>
<li class="">But you must manage IAM permissions to prevent cross-env visibility</li>
</ul>
</li>
<li class=""><strong>Result:</strong> Maximize query utility (cross-service queries) while maintaining permission boundaries</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="services-with-native-hierarchies">Services with Native Hierarchies<a href="https://blog.derrops.com/blog/derrops-conventions#services-with-native-hierarchies" class="hash-link" aria-label="Direct link to Services with Native Hierarchies" title="Direct link to Services with Native Hierarchies" translate="no">​</a></h2>
<table><thead><tr><th>Service</th><th>Hierarchy Type</th><th>Delimiter</th><th>Benefit</th><th>Example</th></tr></thead><tbody><tr><td>S3 Object Keys</td><td>Path-based</td><td><code>/</code></td><td>Prefix filtering, object organization</td><td><code>acme/payments/checkout-api/schema.sql</code></td></tr><tr><td>SSM Parameter Store</td><td>Path-based</td><td><code>/</code></td><td>GetParametersByPath queries, IAM path scoping</td><td><code>/acme/payments/checkout-api/stripe-key</code></td></tr><tr><td>Secrets Manager</td><td>Path-based</td><td><code>/</code></td><td>Organized secrets, prefix filtering</td><td><code>acme/payments/checkout-api/db-password</code></td></tr><tr><td>IAM Paths</td><td>Path-based</td><td><code>/</code></td><td>Permission scoping, least privilege</td><td><code>/acme/payments/checkout-api/</code></td></tr><tr><td>ECR</td><td>Path-based</td><td><code>/</code></td><td>Repository organization</td><td><code>acme/payments/checkout-api</code></td></tr><tr><td>CloudWatch Logs</td><td>Path-based</td><td><code>/</code></td><td>Log group filtering and organization</td><td><code>/acme/payments/checkout-api/logs</code></td></tr><tr><td>CloudWatch Metrics</td><td>Namespace + Dimensions</td><td><code>/</code> (namespace), key-value (dims)</td><td>Namespace: <code>acme/payments</code>; Dimension: <code>service=checkout-api</code>; enables cross-service queries</td><td><code>acme/payments</code> (namespace) + <code>service=checkout-api</code> (dimension)</td></tr><tr><td>OpenSearch</td><td>Index patterns</td><td><code>/</code></td><td>Time-series index organization</td><td><code>acme/payments/checkout-api/transactions/2024-01-15</code></td></tr><tr><td>Route53 DNS</td><td>Subdomain</td><td><code>.</code></td><td>Zone delegation, DNS hierarchy</td><td><code>checkout-api.payments.acme.com</code></td></tr><tr><td>Kafka</td><td>Topic dots</td><td><code>.</code></td><td>Topic organization and consumer scoping</td><td><code>acme.payments.checkout-api.events</code></td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="dns">DNS<a href="https://blog.derrops.com/blog/derrops-conventions#dns" class="hash-link" aria-label="Direct link to DNS" title="Direct link to DNS" translate="no">​</a></h2>
<p>DNS naming is a special case where the hierarchy is reversed compared to prefix-based naming conventions.</p>
<p>Prefix-based systems grow <strong>left → right</strong>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">/{org}/{domain}/{service}</span><br></span></code></pre></div></div>
<p>DNS grows <strong>right → left</strong>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">{service}.{env}.{org}.com</span><br></span></code></pre></div></div>
<p>Both represent the same logical hierarchy in opposite directions.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="core-principle">Core Principle<a href="https://blog.derrops.com/blog/derrops-conventions#core-principle" class="hash-link" aria-label="Direct link to Core Principle" title="Direct link to Core Principle" translate="no">​</a></h3>
<blockquote>
<p>DNS names must preserve the same logical hierarchy as prefixes, but in reverse order.</p>
</blockquote>
<table><thead><tr><th>Prefix</th><th>DNS Equivalent</th></tr></thead><tbody><tr><td><code>/acme/payments/checkout-api</code></td><td><code>checkout-api.payments.acme.com</code></td></tr><tr><td><code>/acme/identity/auth-service</code></td><td><code>auth-service.identity.acme.com</code></td></tr><tr><td><code>/acme/portal/web</code></td><td><code>web.portal.acme.com</code></td></tr></tbody></table>
<p>Hierarchy equivalence:</p>
<table><thead><tr><th>Logical Level</th><th>Prefix</th><th>DNS</th></tr></thead><tbody><tr><td>Org</td><td>acme</td><td>acme.com</td></tr><tr><td>Domain</td><td>payments</td><td>payments.acme.com</td></tr><tr><td>Service</td><td>checkout-api</td><td>checkout-api.payments.acme.com</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="environment-inclusion">Environment Inclusion<a href="https://blog.derrops.com/blog/derrops-conventions#environment-inclusion" class="hash-link" aria-label="Direct link to Environment Inclusion" title="Direct link to Environment Inclusion" translate="no">​</a></h3>
<p>Environment appears in DNS between the service and the org:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">{service}.{env}.{org}.com</span><br></span></code></pre></div></div>
<p>Each environment account is delegated its own subdomain of the apex domain:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">checkout-api.prod.acme.com   (prod account owns prod.acme.com)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">checkout-api.dev.acme.com    (dev account owns dev.acme.com)</span><br></span></code></pre></div></div>
<p>The apex domain (<code>acme.com</code>) is managed in a central network or DNS account. Each environment account is delegated a subdomain (<code>prod.acme.com</code>, <code>dev.acme.com</code>), giving that account full ownership and autonomy over its DNS records. This mirrors how account segregation provides namespace isolation — the subdomain <strong>is</strong> the account's namespace in DNS.</p>
<p>Any new service deployed into an account is automatically under that account's subdomain, with no coordination required at the apex level.</p>
<p>Alternative (apex zone per account, no env in DNS):</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">checkout-api.payments.acme.com</span><br></span></code></pre></div></div>
<p>Same name exists independently in each environment account. Env isolation is provided entirely by the account boundary with no qualifier in the URL.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="route53-hosted-zone-strategy">Route53 Hosted Zone Strategy<a href="https://blog.derrops.com/blog/derrops-conventions#route53-hosted-zone-strategy" class="hash-link" aria-label="Direct link to Route53 Hosted Zone Strategy" title="Direct link to Route53 Hosted Zone Strategy" translate="no">​</a></h3>
<p>Preferred:</p>
<p>The apex domain (<code>acme.com</code>) is managed in a central account. Each environment account is delegated its own subdomain via NS record delegation:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">prod.acme.com   (prod account)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">dev.acme.com    (dev account)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">uat.acme.com    (uat account)</span><br></span></code></pre></div></div>
<p>Services are then addressed as <code>{service}.{env}.{org}.com</code>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">checkout-api.prod.acme.com</span><br></span></code></pre></div></div>
<p>Each account has full autonomy over its subdomain. Adding a new service or record requires no changes to the central apex zone. The apex zone only needs to be updated when a new environment account is onboarded.</p>
<p>Alternative (apex zone per account):</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">acme.com</span><br></span></code></pre></div></div>
<p>Environment isolation is provided entirely by the account boundary. No env qualifier appears in DNS.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="summary">Summary<a href="https://blog.derrops.com/blog/derrops-conventions#summary" class="hash-link" aria-label="Direct link to Summary" title="Direct link to Summary" translate="no">​</a></h3>
<table><thead><tr><th>Prefix Convention</th><th>DNS Convention</th></tr></thead><tbody><tr><td><code>{org}/{domain}/{service}</code></td><td><code>{service}.{env}.{org}.com</code></td></tr><tr><td>Hierarchy grows left → right</td><td>Hierarchy grows right → left</td></tr><tr><td>Namespace via account</td><td>Namespace via env subdomain</td></tr><tr><td>Logical identity preserved</td><td>Logical identity preserved</td></tr></tbody></table>
<p>DNS is not an exception to the naming convention — it is the same hierarchy represented in reverse due to DNS delegation design.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="aws-ssm-parameter-store">AWS SSM Parameter Store<a href="https://blog.derrops.com/blog/derrops-conventions#aws-ssm-parameter-store" class="hash-link" aria-label="Direct link to AWS SSM Parameter Store" title="Direct link to AWS SSM Parameter Store" translate="no">​</a></h2>
<p>SSM Parameter Store uses <code>/</code> as a native path delimiter and supports <code>GetParametersByPath</code> to fetch all parameters under a given prefix. Use it directly as the segment delimiter — it is the hierarchical naming convention with no translation required.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">/{org}/{domain}/{service}/{key}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">/acme/payments/checkout-api/stripe-webhook-secret</span><br></span></code></pre></div></div>
<p>Every prefix is a valid, queryable operational boundary:</p>
<table><thead><tr><th>Path</th><th>Meaning</th></tr></thead><tbody><tr><td><code>/acme/*</code></td><td>All parameters for the org</td></tr><tr><td><code>/acme/payments/*</code></td><td>All parameters owned by payments</td></tr><tr><td><code>/acme/payments/checkout-api/*</code></td><td>All parameters for a specific service</td></tr><tr><td><code>/acme/payments/checkout-api/stripe-webhook-secret</code></td><td>A specific value</td></tr></tbody></table>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</div><div class="admonitionContent_BuS1"><p>IAM policies can scope access using path prefixes. A role for <code>checkout-api</code> can be granted access to only <code>/acme/payments/checkout-api/*</code>, with no wildcard bleed into sibling services or domains.</p></div></div>
<p>If the Parameter Store is <strong>not</strong> account-segregated by environment, add <code>{env}</code> after <code>{org}</code>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">/{org}/{env}/{domain}/{service}/{key}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">/acme/prod/payments/checkout-api/stripe-webhook-secret</span><br></span></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="aws-s3-object-keys">AWS S3 Object Keys<a href="https://blog.derrops.com/blog/derrops-conventions#aws-s3-object-keys" class="hash-link" aria-label="Direct link to AWS S3 Object Keys" title="Direct link to AWS S3 Object Keys" translate="no">​</a></h2>
<p>S3 object keys use <code>/</code> as a logical prefix delimiter. <code>ListObjectsV2</code> accepts a <code>Prefix</code> parameter, making it efficient to list or process objects scoped to any segment boundary.</p>
<p>For most resources (e.g. application artefacts, backups), the standard hierarchy applies:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">{org}/{domain}/{service}/{key}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">acme/payments/checkout-api/schema-v3.sql</span><br></span></code></pre></div></div>
<p>When storing high-volume data such as logs or events — where output is continuous and needs to be queried or processed in discrete chunks — add <code>{partition}</code> before <code>{key}</code>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">{org}/{domain}/{service}/{partition}/{key}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">acme/payments/checkout-api/2024/01/15/14/transactions-00001.json</span><br></span></code></pre></div></div>
<p>In this context <code>{key}</code> is the filename — the same segment, just a different form. <code>{partition}</code> groups many such <code>{key}</code> values into a queryable set.</p>
<p><code>{partition}</code> is the <strong>only</strong> segment permitted to contain internal hierarchical delimiters. This allows a partition to span multiple levels of granularity within a single logical segment — for example, time-series log data is commonly partitioned as <code>{year}/{month}/{day}/{hour}</code>. Every sub-level of the partition remains a valid prefix boundary. All other segments must be flat.</p>
<p>Prefix boundaries remain meaningful at every level, including within the partition itself:</p>
<table><thead><tr><th>Prefix</th><th>Scope</th></tr></thead><tbody><tr><td><code>acme/</code></td><td>All objects for the org</td></tr><tr><td><code>acme/payments/</code></td><td>All objects owned by payments</td></tr><tr><td><code>acme/payments/checkout-api/</code></td><td>All objects for a specific service</td></tr><tr><td><code>acme/payments/checkout-api/2024/</code></td><td>All log data for a given year</td></tr><tr><td><code>acme/payments/checkout-api/2024/01/</code></td><td>All log data for a given month</td></tr><tr><td><code>acme/payments/checkout-api/2024/01/15/</code></td><td>All log data for a given day</td></tr><tr><td><code>acme/payments/checkout-api/2024/01/15/14/</code></td><td>All log data for a specific hour — download or replay that time window</td></tr><tr><td><code>acme/payments/checkout-api/2024/01/15/14/transactions-00001.json</code></td><td>A specific log file</td></tr></tbody></table>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>S3 bucket names are globally unique and require <code>{env}</code> and often <code>{region}</code> in the bucket name itself (see Concrete Examples above). But within the bucket, object key prefixes follow the standard hierarchy without <code>{env}</code> — the bucket name is already the environment boundary.</p></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="aws-iam-paths">AWS IAM Paths<a href="https://blog.derrops.com/blog/derrops-conventions#aws-iam-paths" class="hash-link" aria-label="Direct link to AWS IAM Paths" title="Direct link to AWS IAM Paths" translate="no">​</a></h2>
<p>IAM roles, users, groups, and policies support an optional <code>/path/</code> prefix that can be used to organise resources and scope access via IAM policy conditions (<code>iam:ResourceTag</code> or path-based conditions).</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">/{org}/{domain}/{service}/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">/acme/payments/checkout-api/</span><br></span></code></pre></div></div>
<p>A policy granting cross-service access to everything under payments:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token property" style="color:#36acaa">"Resource"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"arn:aws:iam::*:role/acme/payments/*"</span><br></span></code></pre></div></div>
<table><thead><tr><th>IAM Path</th><th>Scope</th></tr></thead><tbody><tr><td><code>/acme/*</code></td><td>All roles in the org</td></tr><tr><td><code>/acme/payments/*</code></td><td>All roles owned by payments</td></tr><tr><td><code>/acme/payments/checkout-api/*</code></td><td>All roles for a specific service</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="container-image-registries">Container Image Registries<a href="https://blog.derrops.com/blog/derrops-conventions#container-image-registries" class="hash-link" aria-label="Direct link to Container Image Registries" title="Direct link to Container Image Registries" translate="no">​</a></h2>
<p>Container registries such as ECR, Docker Hub, and GHCR use a <code>{registry}/{namespace}/{image}:{tag}</code> structure. Map org and domain onto the namespace hierarchy, with the image tag serving as <code>{key}</code>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">{registry}/{org}/{domain}/{service}:{key}</span><br></span></code></pre></div></div>
<p>The <code>:</code> is the native delimiter for the tag in image references — the same role <code>/</code> plays in path hierarchies. <code>{key}</code> here is the image tag (e.g. <code>1.2.3</code>): the final identifier that specifies exactly which artifact is being pulled.</p>
<p>Examples:</p>
<table><thead><tr><th>Registry</th><th>Example</th></tr></thead><tbody><tr><td>ECR</td><td><code>123456789.dkr.ecr.ap-southeast-2.amazonaws.com/acme/payments/checkout-api:1.2.3</code></td></tr><tr><td>Docker Hub</td><td><code>docker.io/acme/checkout-api:1.2.3</code></td></tr><tr><td>GHCR</td><td><code>ghcr.io/acme/checkout-api:1.2.3</code></td></tr></tbody></table>
<p><code>{key}</code> encodes the version, not the environment. Environment is determined by which account pulls and runs the image, not by the image reference itself.</p>
<p><strong>Cross cutting secrets/config</strong></p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_BuS1"><p>Sometimes there will be cross cutting secrets/config which do not belong to any one particular domain, service or organization. Such as if the organization has purchased a service to be used by 2 different orgs. If this configuration cannot be definitively assigned to one or the other, then it should be stored in a cross cutting location, and permissions will need to be explicitly granted, rather than relying on prefix conventions.</p></div></div>
<p><strong>Logical Name vs Physical Name</strong></p>
<p>Logical Name:
checkout-api</p>
<p>Physical Deployment Instance:
Account: prod
Region: ap-southeast-2
Name: checkout-api</p>
<p>Fully Qualified Identity:
ap-southeast-2/prod/checkout-api</p>
<p>Name remains constant. Namespace varies.</p>
<p><strong>Deployment Instance</strong></p>
<p>A Deployment Instance is a concrete runtime instantiation of a logical resource within a specific namespace.</p>
<p>Example:</p>
<p>Logical Resource: checkout-api</p>
<p>Deployment Instances:</p>
<ul>
<li class="">Account: dev → checkout-api</li>
<li class="">Account: prod → checkout-api</li>
<li class="">Account: uat → checkout-api</li>
</ul>
<p>All share the same logical name but exist in different namespaces.</p>]]></content:encoded>
            <category>devops</category>
            <category>aws</category>
        </item>
        <item>
            <title><![CDATA[Naming Cheatsheet for best Pracices]]></title>
            <link>https://blog.derrops.com/blog/derrops-naming-sheet</link>
            <guid>https://blog.derrops.com/blog/derrops-naming-sheet</guid>
            <pubDate>Thu, 26 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Quick reference guide for naming AWS resources following the Derrops conventions. All examples assume account-segregated environments (preferred approach).]]></description>
            <content:encoded><![CDATA[<p>Quick reference guide for naming AWS resources following the Derrops conventions. All examples assume <strong>account-segregated environments</strong> (preferred approach).</p>
<p><strong>Core Format:</strong> <code>{org}--{domain}--{service}--{key}</code> (compound kebab-case with <code>--</code> segment delimiters)</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="template-variables-reference">Template Variables Reference<a href="https://blog.derrops.com/blog/derrops-naming-sheet#template-variables-reference" class="hash-link" aria-label="Direct link to Template Variables Reference" title="Direct link to Template Variables Reference" translate="no">​</a></h2>
<p>Before using this cheatsheet, understand what each placeholder means:</p>
<table><thead><tr><th>Variable</th><th>Definition</th><th>Example</th><th>Required?</th><th>Notes</th></tr></thead><tbody><tr><td><code>{region}</code></td><td>AWS region code</td><td><code>ap-southeast-2</code>, <code>us-east-1</code>, <code>eu-west-1</code></td><td>✅ Only for globally unique services (S3, CloudFront, ACM, Route53)</td><td>Omit if using account-per-region segregation</td></tr><tr><td><code>{env}</code></td><td>Deployment environment</td><td><code>prod</code>, <code>dev</code>, <code>staging</code>, <code>uat</code></td><td>✅ Only for globally unique services (S3) or DNS</td><td>Omit if using account-per-environment segregation (recommended)</td></tr><tr><td><code>{org}</code></td><td>Organization/top-level business unit</td><td><code>acme</code>, <code>mycompany</code>, <code>client-name</code></td><td>✅ Always required</td><td>Most stable segment; rarely changes</td></tr><tr><td><code>{domain}</code></td><td>Business capability / bounded domain</td><td><code>payments</code>, <code>identity</code>, <code>analytics</code>, <code>platform</code></td><td>✅ Always required</td><td>Owned independently; more stable than teams</td></tr><tr><td><code>{service}</code></td><td>Deployable service unit</td><td><code>checkout-api</code>, <code>auth-service</code>, <code>webhook-worker</code></td><td>✅ Always required</td><td>The primary identity; can be renamed/refactored</td></tr><tr><td><code>{key}</code></td><td>Specific resource or config within service</td><td><code>transactions</code>, <code>webhook-secret</code>, <code>primary</code>, <code>cache</code></td><td>✅ Always required</td><td>Purpose-specific identifier; changes frequently</td></tr><tr><td><code>{partition}</code></td><td>Data partition grouping (logs, events only)</td><td><code>2024/01/15/14</code>, <code>2024-01-15</code></td><td>❌ Optional; data storage only</td><td>Only for time-series or partitioned data in S3/Glue</td></tr><tr><td><code>{purpose}</code></td><td>Functional purpose</td><td><code>alb</code>, <code>db</code>, <code>lambda</code>, <code>encryption-enabled</code></td><td>✅ When needed for clarity</td><td>Qualifies the resource type</td></tr><tr><td><code>{type}</code></td><td>Resource subtype</td><td><code>web</code>, <code>worker</code>, <code>private</code>, <code>public</code>, <code>primary</code>, <code>replica</code></td><td>✅ When distinguishing variants</td><td>Makes specific instances identifiable</td></tr><tr><td><code>{az}</code></td><td>Availability zone</td><td><code>1a</code>, <code>1b</code>, <code>1c</code></td><td>✅ For multi-AZ resources</td><td>Ensures subnets are distributed</td></tr><tr><td><code>{consumer}</code></td><td>API consumer/client</td><td><code>mobile-client</code>, <code>partner-integrations</code>, <code>internal</code></td><td>✅ For API keys and access</td><td>Identifies who consumes the resource</td></tr><tr><td><code>{target}</code></td><td>Target system for data source</td><td><code>dynamodb</code>, <code>rds</code>, <code>lambda</code>, <code>s3</code></td><td>✅ For integration points</td><td>What the resource connects to</td></tr><tr><td><code>{num}</code></td><td>Sequential number</td><td><code>01</code>, <code>02</code>, <code>03</code></td><td>❌ Optional; for instance naming</td><td>Zero-padded for sorting</td></tr><tr><td><code>{yyyy}/{mm}/{dd}/{hh}</code></td><td>Date/time partitions</td><td><code>2024/01/15/14</code></td><td>✅ For time-series data only</td><td>Each level is independently queryable</td></tr><tr><td><code>{file}</code></td><td>Filename</td><td><code>transactions.json</code>, <code>logs.parquet</code></td><td>✅ For object/file storage</td><td>The final artifact identifier</td></tr><tr><td><code>{version}</code> or <code>{tag}</code></td><td>Semantic version or release tag</td><td><code>1.2.3</code>, <code>latest</code>, <code>v2.0.0-beta</code></td><td>✅ For images and artifacts</td><td>Identifies specific release</td></tr><tr><td><code>{registry}</code></td><td>Container registry host</td><td><code>123456789.dkr.ecr.ap-southeast-2.amazonaws.com</code>, <code>docker.io</code></td><td>✅ For container images</td><td>Where the image is hosted</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="summary-table---all-services">Summary Table - All Services<a href="https://blog.derrops.com/blog/derrops-naming-sheet#summary-table---all-services" class="hash-link" aria-label="Direct link to Summary Table - All Services" title="Direct link to Summary Table - All Services" translate="no">​</a></h2>
<table><thead><tr><th>Service</th><th>Format</th><th>Pattern</th><th>Delimiter</th><th>Example</th></tr></thead><tbody><tr><td><strong>S3 Bucket</strong></td><td>Global + prefix</td><td><code>ap-southeast-2--prod--{org}--{domain}--{service}--{key}</code></td><td><code>-</code></td><td><code>ap-southeast-2--prod--acme--payments--checkout-api--backups</code></td></tr><tr><td><strong>S3 Object Keys</strong></td><td>Hierarchy</td><td><code>{org}/{domain}/{service}/{key}</code></td><td><code>/</code></td><td><code>acme/payments/checkout-api/schema.sql</code></td></tr><tr><td><strong>S3 Logs/Events</strong></td><td>With partition</td><td><code>{org}/{domain}/{service}/{yyyy}/{mm}/{dd}/{hh}/{file}</code></td><td><code>/</code></td><td><code>acme/payments/checkout-api/2024/01/15/14/transactions-00001.json</code></td></tr><tr><td><strong>CloudWatch Logs</strong></td><td>Hierarchy</td><td><code>/{org}/{domain}/{service}/{key}</code></td><td><code>/</code></td><td><code>/acme/payments/checkout-api/application-logs</code></td></tr><tr><td><strong>CloudWatch Metrics (Namespace)</strong></td><td>Hierarchy (org/domain only)</td><td><code>{org}/{domain}</code></td><td><code>/</code></td><td><code>acme/payments</code></td></tr><tr><td><strong>CloudWatch Metric (Dimensions)</strong></td><td>Key-value pairs</td><td><code>service={service}</code> (env via account, not dimension)</td><td>N/A</td><td><code>service=checkout-api</code>, <code>service=order-processor</code></td></tr><tr><td><strong>CloudWatch Metric Names</strong></td><td>Flat kebab</td><td><code>{key}-{metric-type}</code></td><td><code>-</code></td><td><code>request-count</code>, <code>error-rate</code>, <code>latency-p99</code></td></tr><tr><td><strong>ECS Cluster</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{key}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--cluster</code></td></tr><tr><td><strong>ECS Service</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api</code></td></tr><tr><td><strong>ECS Task Definition</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api</code></td></tr><tr><td><strong>ECR Repository</strong></td><td>Registry path</td><td><code>{org}/{domain}/{service}</code></td><td><code>/</code></td><td><code>acme/payments/checkout-api</code></td></tr><tr><td><strong>ECR Image Tag</strong></td><td>Semantic</td><td><code>{registry}/{org}/{domain}/{service}:{version}</code></td><td><code>/ :</code></td><td><code>123456789.dkr.ecr.ap-southeast-2.amazonaws.com/acme/payments/checkout-api:1.2.3</code></td></tr><tr><td><strong>DynamoDB Table</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{key}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--transactions</code></td></tr><tr><td><strong>DynamoDB GSI</strong></td><td>Flat kebab</td><td><code>{key}--gsi</code></td><td><code>--</code> / <code>-</code></td><td><code>transactions-by-user--gsi</code></td></tr><tr><td><strong>RDS Instance ID</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{key}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--primary</code></td></tr><tr><td><strong>RDS DB Name</strong></td><td>Flat snake</td><td><code>{org}_{domain}_{service}</code></td><td><code>_</code></td><td><code>acme_payments_checkout_api</code></td></tr><tr><td><strong>RDS Parameter Group</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{key}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--params</code></td></tr><tr><td><strong>RDS Subnet Group</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--subnet-group</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--subnet-group</code></td></tr><tr><td><strong>EC2 Instance</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{type}-{num}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--web-01</code></td></tr><tr><td><strong>EC2 Security Group</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{purpose}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--alb</code></td></tr><tr><td><strong>EC2 Volume</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--volume-{purpose}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--volume-data</code></td></tr><tr><td><strong>EC2 Elastic IP</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--eip</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--eip</code></td></tr><tr><td><strong>Lambda Function</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{key}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--webhook-handler</code></td></tr><tr><td><strong>Lambda Layer</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{purpose}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--shared-utilities--common-libs</code></td></tr><tr><td><strong>Lambda Alias</strong></td><td>Simple</td><td><code>prod</code>, <code>dev</code>, <code>staging</code></td><td>N/A</td><td><code>prod</code></td></tr><tr><td><strong>IAM Role</strong></td><td>Path + name</td><td>Path: <code>/{org}/{domain}/{service}/</code> Name: <code>{service}--{purpose}-role</code></td><td><code>/</code> <code>--</code> <code>-</code></td><td>ARN: <code>arn:aws:iam::123456789:role/acme/payments/checkout-api/checkout-api--lambda-role</code></td></tr><tr><td><strong>IAM Policy</strong></td><td>Path + name</td><td>Path: <code>/{org}/{domain}/{service}/</code> Name: <code>{purpose}-policy</code></td><td><code>/</code> <code>-</code></td><td><code>acme--payments--checkout-api--s3-access-policy</code></td></tr><tr><td><strong>IAM User</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--user</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--service-user</code></td></tr><tr><td><strong>Route53 Hosted Zone</strong></td><td>Subdomain</td><td><code>prod.acme.com</code> (env from account)</td><td><code>.</code></td><td><code>prod.acme.com</code></td></tr><tr><td><strong>Route53 DNS Record</strong></td><td>Reverse hierarchy</td><td><code>{service}.prod.acme.com</code></td><td><code>.</code></td><td><code>checkout-api.prod.acme.com</code></td></tr><tr><td><strong>Route53 Private Zone</strong></td><td>Internal DNS</td><td><code>{service}.internal.prod.acme.com</code></td><td><code>.</code></td><td><code>checkout-api.internal.prod.acme.com</code></td></tr><tr><td><strong>CloudFront Distribution</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--cdn</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--cdn</code></td></tr><tr><td><strong>CloudFront Alias (CNAME)</strong></td><td>DNS pattern</td><td><code>{service}.prod.acme.com</code></td><td><code>.</code></td><td><code>checkout-api.prod.acme.com</code></td></tr><tr><td><strong>ACM Certificate Domain</strong></td><td>DNS pattern</td><td><code>{service}.prod.acme.com</code></td><td><code>.</code></td><td><code>checkout-api.prod.acme.com</code></td></tr><tr><td><strong>ACM Wildcard Cert</strong></td><td>DNS wildcard</td><td><code>*.prod.acme.com</code></td><td><code>.</code></td><td><code>*.prod.acme.com</code></td></tr><tr><td><strong>VPC</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--vpc</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--vpc</code></td></tr><tr><td><strong>Subnet</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--subnet-{type}-{az}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--subnet-private-1a</code></td></tr><tr><td><strong>Route Table</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--rt-{type}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--rt-private</code></td></tr><tr><td><strong>Network ACL</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--nacl</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--nacl</code></td></tr><tr><td><strong>ALB/NLB</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--alb</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--alb</code></td></tr><tr><td><strong>Target Group</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--tg-{purpose}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--tg-api</code></td></tr><tr><td><strong>SNS Topic</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{key}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--transactions</code></td></tr><tr><td><strong>SQS Queue</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{key}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--events</code></td></tr><tr><td><strong>SQS FIFO Queue</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{key}.fifo</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--events.fifo</code></td></tr><tr><td><strong>SQS DLQ</strong></td><td>Flat kebab</td><td><code>{queue-name}--dlq</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--events--dlq</code></td></tr><tr><td><strong>Kinesis Stream</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{key}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--events</code></td></tr><tr><td><strong>EventBridge Bus</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--events</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--events</code></td></tr><tr><td><strong>EventBridge Rule</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{key}-rule</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--process-webhook-rule</code></td></tr><tr><td><strong>Step Functions</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{key}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--order-processing</code></td></tr><tr><td><strong>API Gateway REST API</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--api</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--api</code></td></tr><tr><td><strong>API Gateway HTTP API</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--http-api</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--http-api</code></td></tr><tr><td><strong>API Gateway Stage</strong></td><td>Simple</td><td><code>prod</code>, <code>dev</code>, <code>staging</code></td><td>N/A</td><td><code>prod</code></td></tr><tr><td><strong>API Gateway Key</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{consumer}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--mobile-client</code></td></tr><tr><td><strong>AppSync API</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--api</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--api</code></td></tr><tr><td><strong>AppSync Data Source</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{target}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--dynamodb</code></td></tr><tr><td><strong>ElastiCache Cluster</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--cache</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--cache</code></td></tr><tr><td><strong>ElastiCache Replication Group</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--replication-group</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--replication-group</code></td></tr><tr><td><strong>ElastiCache Parameter Group</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--params</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--params</code></td></tr><tr><td><strong>OpenSearch Domain</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api</code></td></tr><tr><td><strong>OpenSearch Index</strong></td><td>Hierarchy</td><td><code>{org}/{domain}/{service}/{key}/{date}</code></td><td><code>/</code></td><td><code>acme/payments/checkout-api/transactions/2024-01-15</code></td></tr><tr><td><strong>RDS Proxy</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--proxy</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--proxy</code></td></tr><tr><td><strong>AWS Backup Plan</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--backup-plan</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--backup-plan</code></td></tr><tr><td><strong>AWS Backup Vault</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--vault</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--vault</code></td></tr><tr><td><strong>Glue Database</strong></td><td>Flat snake</td><td><code>{org}_{domain}_{service}</code></td><td><code>_</code></td><td><code>acme_payments_checkout_api</code></td></tr><tr><td><strong>Glue Table</strong></td><td>Flat snake</td><td><code>{key}</code></td><td>N/A</td><td><code>transactions</code></td></tr><tr><td><strong>Glue Job</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{key}-job</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--analytics--etl--transform-job</code></td></tr><tr><td><strong>Glue Crawler</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{key}-crawler</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--analytics--data-crawlers</code></td></tr><tr><td><strong>Athena Workgroup</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--workgroup</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--analytics--etl--workgroup</code></td></tr><tr><td><strong>Athena Results Bucket</strong></td><td>S3 key path</td><td><code>s3://bucket/{org}/{domain}/{service}/</code></td><td><code>/</code></td><td><code>s3://acme-analytics-athena-results/acme/analytics/etl/</code></td></tr><tr><td><strong>QuickSight Dataset</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--dataset</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--analytics--transactions--dataset</code></td></tr><tr><td><strong>QuickSight Analysis</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{key}-analysis</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--analytics--revenue-dashboard--analysis</code></td></tr><tr><td><strong>QuickSight Dashboard</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{key}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--analytics--revenue-dashboard</code></td></tr><tr><td><strong>Redshift Cluster</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--cluster</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--analytics--warehouse--cluster</code></td></tr><tr><td><strong>Redshift Database</strong></td><td>Flat snake</td><td><code>{org}_{domain}_{service}</code></td><td><code>_</code></td><td><code>acme_analytics_warehouse</code></td></tr><tr><td><strong>Redshift Subnet Group</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--subnet-group</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--analytics--warehouse--subnet-group</code></td></tr><tr><td><strong>MSK Cluster</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--cluster</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--events--streaming--cluster</code></td></tr><tr><td><strong>Kafka Topic</strong></td><td>Dotted path</td><td><code>{org}.{domain}.{service}.{key}</code></td><td><code>.</code></td><td><code>acme.payments.checkout-api.transactions</code></td></tr><tr><td><strong>AppConfig Application</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api</code></td></tr><tr><td><strong>AppConfig Environment</strong></td><td>Simple</td><td><code>prod</code>, <code>dev</code>, <code>staging</code></td><td>N/A</td><td><code>prod</code></td></tr><tr><td><strong>AppConfig Profile</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{key}-profile</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--feature-flags-profile</code></td></tr><tr><td><strong>Systems Manager Document</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{key}</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--runbook</code></td></tr><tr><td><strong>Systems Manager Maintenance Window</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--maintenance</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--patching-window</code></td></tr><tr><td><strong>Service Catalog Portfolio</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--portfolio</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--portfolio</code></td></tr><tr><td><strong>Service Catalog Product</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--product</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--product</code></td></tr><tr><td><strong>X-Ray Sampling Rule</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--sampling-rule</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--sampling-rule</code></td></tr><tr><td><strong>Config Rule</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{key}-rule</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--encryption-enabled-rule</code></td></tr><tr><td><strong>Config Aggregator</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--config-aggregator</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--config-aggregator</code></td></tr><tr><td><strong>Security Hub Custom Insight</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{key}-insight</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--critical-findings-insight</code></td></tr><tr><td><strong>WAF Web ACL</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--waf</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--waf</code></td></tr><tr><td><strong>WAF IP Set</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{purpose}-ipset</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--blocked-ips</code></td></tr><tr><td><strong>WAF Rule Group</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--rules</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--rate-limit-rules</code></td></tr><tr><td><strong>CloudFormation Stack</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--{key}-stack</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--stack</code></td></tr><tr><td><strong>SSM Parameter</strong></td><td>Hierarchy</td><td><code>/{org}/{domain}/{service}/{key}</code></td><td><code>/</code></td><td><code>/acme/payments/checkout-api/stripe-webhook-secret</code></td></tr><tr><td><strong>Secrets Manager Secret</strong></td><td>Hierarchy</td><td><code>{org}/{domain}/{service}/{key}</code></td><td><code>/</code></td><td><code>acme/payments/checkout-api/db-password</code></td></tr><tr><td><strong>Auto Scaling Group</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--asg</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--asg</code></td></tr><tr><td><strong>Launch Template</strong></td><td>Flat kebab</td><td><code>{org}--{domain}--{service}--launch-template</code></td><td><code>--</code> / <code>-</code></td><td><code>acme--payments--checkout-api--launch-template</code></td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="️-critical-native-hierarchy-detection">⚠️ Critical: Native Hierarchy Detection<a href="https://blog.derrops.com/blog/derrops-naming-sheet#%EF%B8%8F-critical-native-hierarchy-detection" class="hash-link" aria-label="Direct link to ⚠️ Critical: Native Hierarchy Detection" title="Direct link to ⚠️ Critical: Native Hierarchy Detection" translate="no">​</a></h2>
<p><strong>Many services support their own native hierarchical constructs.</strong> Always check if a service has native hierarchy support BEFORE applying <code>--</code> segment delimiters. Using native hierarchies enables:</p>
<ul>
<li class="">Prefix filtering and querying</li>
<li class="">Permission scoping via path-based policies</li>
<li class="">Better operational organization</li>
<li class="">Automatic drill-down capabilities in console</li>
</ul>
<p><strong>Example:</strong> CloudWatch metrics require careful structure:</p>
<ul>
<li class=""><strong>Namespace</strong> uses <code>/</code> for org/domain ONLY (e.g., <code>acme/payments</code>) ← <strong>USE THIS FOR HIERARCHY</strong></li>
<li class=""><strong>Dimensions</strong> include <code>service={service}</code> (e.g., <code>service=checkout-api</code>); env via account boundary ← <strong>USE THIS FOR CROSS-SERVICE QUERIES</strong></li>
<li class=""><strong>Metric names</strong> use <code>-</code> for words only (e.g., <code>request-count</code>, <code>error-rate</code>) ← Use this for specifics</li>
</ul>
<p>Why? This enables meaningful queries like "all services in payments domain with high CPU" by filtering the service dimension, not namespaces. Each account's metrics are naturally isolated, maintaining permission boundaries while maximizing query utility!</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="delimiter-decision-matrix">Delimiter Decision Matrix<a href="https://blog.derrops.com/blog/derrops-naming-sheet#delimiter-decision-matrix" class="hash-link" aria-label="Direct link to Delimiter Decision Matrix" title="Direct link to Delimiter Decision Matrix" translate="no">​</a></h2>
<table><thead><tr><th>Use Case</th><th>Default Delimiter</th><th>Notes</th><th>When to Override</th><th>Services</th></tr></thead><tbody><tr><td><strong>Segment separator</strong> (org↔domain↔service↔key)</td><td><code>--</code> (double hyphen)</td><td>Separates major naming segments; most readable</td><td><strong>ALWAYS check for native hierarchy first</strong></td><td>All flat resource names</td></tr><tr><td><strong>Word within segment</strong></td><td><code>-</code> (hyphen)</td><td>Words/parts within a single segment</td><td>Never—always use <code>-</code> for words</td><td>All resource names</td></tr><tr><td><strong>Path hierarchy (native)</strong></td><td><code>/</code> (slash)</td><td>Native hierarchical support—use instead of <code>--</code></td><td><strong>Use <code>/</code> instead of <code>--</code> when available</strong></td><td>S3 keys, SSM Parameters, Secrets, IAM paths, ECR, CloudWatch Logs, CloudWatch Metrics namespaces</td></tr><tr><td><strong>DNS hierarchy (native)</strong></td><td><code>.</code> (dot)</td><td>Native DNS subdomain separation—use instead of <code>--</code></td><td><strong>Use <code>.</code> instead of <code>--</code> when available</strong></td><td>Route53, DNS records, CloudFront aliases, Kafka topics</td></tr><tr><td><strong>Image tag/version (native)</strong></td><td><code>:</code> (colon)</td><td>Native registry delimiter for versioning</td><td>Use <code>:</code> after image name</td><td>ECR, Docker registries</td></tr><tr><td><strong>Database internal names</strong></td><td><code>_</code> (underscore)</td><td>DB-friendly; only for internal schema names, NOT identifiers</td><td>Use <code>_</code> instead of <code>-</code> for DB/schema names only</td><td>RDS database names, Glue databases</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="global-vs-regional-scope">Global vs Regional Scope<a href="https://blog.derrops.com/blog/derrops-naming-sheet#global-vs-regional-scope" class="hash-link" aria-label="Direct link to Global vs Regional Scope" title="Direct link to Global vs Regional Scope" translate="no">​</a></h2>
<table><thead><tr><th>Resource Type</th><th>Scope</th><th>Includes region?</th><th>Includes env?</th><th>Example</th></tr></thead><tbody><tr><td>S3 Buckets</td><td>Globally unique</td><td>✅ Yes (ap-southeast-2)</td><td>✅ Yes (prod)</td><td><code>ap-southeast-2--prod--acme--payments--checkout-api--backups</code></td></tr><tr><td>Route53 Hosted Zones</td><td>Global DNS</td><td>❌ No</td><td>✅ Via account (prod.acme.com)</td><td><code>prod.acme.com</code></td></tr><tr><td>CloudFront Distributions</td><td>Global CDN</td><td>❌ No</td><td>✅ Via DNS (prod)</td><td><code>checkout-api.prod.acme.com</code></td></tr><tr><td>ACM Certificates</td><td>Global DNS</td><td>❌ No</td><td>✅ Via DNS (prod)</td><td><code>checkout-api.prod.acme.com</code></td></tr><tr><td>IAM Roles</td><td>Global (within account)</td><td>❌ No</td><td>❌ No (via account)</td><td><code>/acme/payments/checkout-api/checkout-api-role</code></td></tr><tr><td>All Regional Services</td><td>Regional</td><td>❌ No (via account)</td><td>❌ No (via account)</td><td><code>acme-payments-checkout-api</code></td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="native-hierarchy-support">Native Hierarchy Support<a href="https://blog.derrops.com/blog/derrops-naming-sheet#native-hierarchy-support" class="hash-link" aria-label="Direct link to Native Hierarchy Support" title="Direct link to Native Hierarchy Support" translate="no">​</a></h2>
<p><strong>PRIORITY: Always use native hierarchies when available.</strong> They provide operational benefits flat names cannot.</p>
<table><thead><tr><th>Service</th><th>Native Support</th><th>Delimiter</th><th>Example</th><th>Benefit</th></tr></thead><tbody><tr><td><strong>S3 Object Keys</strong></td><td>✅ YES</td><td><code>/</code></td><td><code>acme/payments/checkout-api/schema.sql</code></td><td>Prefix filtering, drill-down in console</td></tr><tr><td><strong>SSM Parameter Store</strong></td><td>✅ YES</td><td><code>/</code></td><td><code>/acme/payments/checkout-api/stripe-key</code></td><td>GetParametersByPath queries, IAM scoping</td></tr><tr><td><strong>Secrets Manager</strong></td><td>✅ YES</td><td><code>/</code></td><td><code>acme/payments/checkout-api/db-password</code></td><td>Prefix filtering, organized in console</td></tr><tr><td><strong>IAM Paths</strong></td><td>✅ YES</td><td><code>/</code></td><td><code>/acme/payments/checkout-api/</code></td><td>Path-based IAM policies, permission scoping</td></tr><tr><td><strong>ECR Repositories</strong></td><td>✅ YES</td><td><code>/</code></td><td><code>acme/payments/checkout-api</code></td><td>Namespace organization in console</td></tr><tr><td><strong>CloudWatch Logs</strong></td><td>✅ YES</td><td><code>/</code></td><td><code>/acme/payments/checkout-api/logs</code></td><td>Log group filtering and organization</td></tr><tr><td><strong>CloudWatch Metrics (Namespace)</strong></td><td>✅ YES</td><td><code>/</code> (org/domain only)</td><td><code>acme/payments</code></td><td>Namespace filtering; <code>service</code> as dimension enables cross-service queries</td></tr><tr><td><strong>OpenSearch Indices</strong></td><td>✅ YES</td><td><code>/</code></td><td><code>acme/payments/checkout-api/transactions/2024-01-15</code></td><td>Index pattern matching, time-series organization</td></tr><tr><td><strong>Route53 DNS</strong></td><td>✅ YES</td><td><code>.</code></td><td><code>checkout-api.payments.acme.com</code></td><td>DNS delegation, zone scoping</td></tr><tr><td><strong>Kafka Topics</strong></td><td>✅ YES</td><td><code>.</code></td><td><code>acme.payments.checkout-api.events</code></td><td>Topic organization, consumer group scoping</td></tr><tr><td><strong>DynamoDB Tables</strong></td><td>❌ NO</td><td><code>-</code></td><td>Use <code>{org}--{domain}--{service}--{key}</code></td><td>No hierarchy support; use <code>--</code> delimiters</td></tr><tr><td><strong>RDS Instances</strong></td><td>❌ NO</td><td><code>-</code></td><td>Use <code>{org}--{domain}--{service}--{key}</code></td><td>No hierarchy support; use <code>--</code> delimiters</td></tr><tr><td><strong>Lambda Functions</strong></td><td>❌ NO</td><td><code>-</code></td><td>Use <code>{org}--{domain}--{service}--{key}</code></td><td>No hierarchy support; use <code>--</code> delimiters</td></tr><tr><td><strong>ECS/EC2 Resources</strong></td><td>❌ NO</td><td><code>-</code></td><td>Use <code>{org}--{domain}--{service}--{key}</code></td><td>No hierarchy support; use <code>--</code> delimiters</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="common-pitfalls--solutions">Common Pitfalls &amp; Solutions<a href="https://blog.derrops.com/blog/derrops-naming-sheet#common-pitfalls--solutions" class="hash-link" aria-label="Direct link to Common Pitfalls &amp; Solutions" title="Direct link to Common Pitfalls &amp; Solutions" translate="no">​</a></h2>
<table><thead><tr><th>Pitfall</th><th>Problem</th><th>Why It Matters</th><th>Solution</th></tr></thead><tbody><tr><td>Using <code>_</code> in S3 bucket names</td><td>S3 rejects underscores</td><td>S3 bucket naming constraint</td><td>Use <code>-</code> (hyphens) everywhere</td></tr><tr><td>Inconsistent names across environments</td><td>Cannot query resources across envs</td><td>Breaks filtering in CloudWatch, Config, Security Hub</td><td>Use identical logical names in all accounts</td></tr><tr><td>Including <code>{env}</code> when account-segregated</td><td>Redundant naming; violates consistency principle</td><td>Names become longer, harder to read; breaks queries</td><td>Omit <code>{env}</code> if managing via account boundaries</td></tr><tr><td>Randomly suffixed names</td><td>Names become unpredictable</td><td>Makes automation fragile; breaks IaC</td><td>Use account/region namespace for uniqueness</td></tr><tr><td>Changing <code>{org}</code> or <code>{domain}</code></td><td>Breaks all downstream references and policies</td><td>All IAM policies, CloudWatch filters, and automation fail</td><td>Keep these segments stable; only change <code>{service}</code></td></tr><tr><td>Not using native delimiters</td><td>Loses prefix querying capability</td><td>Cannot use S3 prefix filtering, SSM GetParametersByPath</td><td>Use <code>/</code> for hierarchical systems, <code>.</code> for DNS</td></tr><tr><td>DNS names don't mirror resources</td><td>Routing confusion; cross-team coordination failure</td><td>Applications cannot find correct endpoints</td><td>DNS = reversed hierarchy (service.domain.org.com)</td></tr><tr><td>Resource name &gt; service character limit</td><td>Truncation breaks convention</td><td>Names get auto-truncated; cannot predict final name</td><td>Test limits early; use shorter domain/service names</td></tr><tr><td>Mixed kebab and snake case</td><td>Tools cannot parse consistently</td><td>Scripts fail; team confusion; automation breaks</td><td>Use kebab-case (<code>-</code>) for all naming except DB internals</td></tr><tr><td>Forgetting tagging for cost allocation</td><td>Cannot allocate costs accurately</td><td>Wrong cost attribution; misleading cost reports</td><td>Tag every resource with CostCenter, Owner, Service</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="quick-reference-by-layer">Quick Reference by Layer<a href="https://blog.derrops.com/blog/derrops-naming-sheet#quick-reference-by-layer" class="hash-link" aria-label="Direct link to Quick Reference by Layer" title="Direct link to Quick Reference by Layer" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="infrastructure-layer">Infrastructure Layer<a href="https://blog.derrops.com/blog/derrops-naming-sheet#infrastructure-layer" class="hash-link" aria-label="Direct link to Infrastructure Layer" title="Direct link to Infrastructure Layer" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">VPC: acme--payments--checkout-api--vpc</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Subnet: acme--payments--checkout-api--subnet-private-1a</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Security Group: acme--payments--checkout-api--alb</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="compute-layer">Compute Layer<a href="https://blog.derrops.com/blog/derrops-naming-sheet#compute-layer" class="hash-link" aria-label="Direct link to Compute Layer" title="Direct link to Compute Layer" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ECS Cluster: acme--payments--checkout-api--cluster</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ECS Service: acme--payments--checkout-api</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EC2 Instance: acme--payments--checkout-api--web-01</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Lambda: acme--payments--checkout-api--webhook-handler</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="data-layer">Data Layer<a href="https://blog.derrops.com/blog/derrops-naming-sheet#data-layer" class="hash-link" aria-label="Direct link to Data Layer" title="Direct link to Data Layer" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">DynamoDB: acme--payments--checkout-api--transactions</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">RDS Instance: acme--payments--checkout-api--primary</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">RDS Database: acme_payments_checkout_api</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ElastiCache: acme--payments--checkout-api--cache</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">S3 Bucket: ap-southeast-2--prod--acme--payments--checkout-api--data</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="messaging-layer">Messaging Layer<a href="https://blog.derrops.com/blog/derrops-naming-sheet#messaging-layer" class="hash-link" aria-label="Direct link to Messaging Layer" title="Direct link to Messaging Layer" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">SNS Topic: acme--payments--checkout-api--transactions</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">SQS Queue: acme--payments--checkout-api--events</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">SQS DLQ: acme--payments--checkout-api--events--dlq</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Kinesis Stream: acme--payments--checkout-api--events</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="integration-layer">Integration Layer<a href="https://blog.derrops.com/blog/derrops-naming-sheet#integration-layer" class="hash-link" aria-label="Direct link to Integration Layer" title="Direct link to Integration Layer" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">API Gateway: acme--payments--checkout-api--api</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Step Functions: acme--payments--checkout-api--order-processing</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EventBridge Rule: acme--payments--checkout-api--process-webhook-rule</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="observability-layer">Observability Layer<a href="https://blog.derrops.com/blog/derrops-naming-sheet#observability-layer" class="hash-link" aria-label="Direct link to Observability Layer" title="Direct link to Observability Layer" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">CloudWatch Logs: /acme/payments/checkout-api/application-logs</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">CloudWatch Metrics:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Namespace: acme/payments (org/domain only)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Dimensions: service=checkout-api (env via account boundary)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Metric Name: request-count, error-rate, latency-p99</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Example Query: "All high-CPU services in payments" → Query namespace acme/payments, filter by service dimension</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  Permission Boundary: Account segregation; no env dimension needed</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">X-Ray Rule: acme--payments--checkout-api--sampling-rule</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Config Rule: acme--payments--checkout-api--encryption-enabled-rule</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="security-layer">Security Layer<a href="https://blog.derrops.com/blog/derrops-naming-sheet#security-layer" class="hash-link" aria-label="Direct link to Security Layer" title="Direct link to Security Layer" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">IAM Role: /acme/payments/checkout-api/checkout-api--lambda-role</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">IAM Policy: acme--payments--checkout-api--s3-access-policy</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">WAF Web ACL: acme--payments--checkout-api--waf</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ACM Certificate: checkout-api.prod.acme.com</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="dns-layer">DNS Layer<a href="https://blog.derrops.com/blog/derrops-naming-sheet#dns-layer" class="hash-link" aria-label="Direct link to DNS Layer" title="Direct link to DNS Layer" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">Hosted Zone: prod.acme.com</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">DNS Record: checkout-api.prod.acme.com</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Route53 Private Zone: checkout-api.internal.prod.acme.com</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">CloudFront Alias: checkout-api.prod.acme.com</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="storageconfig-layer">Storage/Config Layer<a href="https://blog.derrops.com/blog/derrops-naming-sheet#storageconfig-layer" class="hash-link" aria-label="Direct link to Storage/Config Layer" title="Direct link to Storage/Config Layer" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">S3 Object Key: acme/payments/checkout-api/schema.sql</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">SSM Parameter: /acme/payments/checkout-api/stripe-webhook-secret</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Secrets Manager: acme/payments/checkout-api/db-password</span><br></span></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="tagging-strategy">Tagging Strategy<a href="https://blog.derrops.com/blog/derrops-naming-sheet#tagging-strategy" class="hash-link" aria-label="Direct link to Tagging Strategy" title="Direct link to Tagging Strategy" translate="no">​</a></h2>
<p>Apply these tags to <strong>all</strong> resources for cost allocation and resource management:</p>
<table><thead><tr><th>Tag Key</th><th>Value</th><th>Example</th><th>Purpose</th></tr></thead><tbody><tr><td><code>org</code></td><td>Organization</td><td><code>acme</code></td><td>Top-level ownership</td></tr><tr><td><code>domain</code></td><td>Business domain</td><td><code>payments</code></td><td>Capability boundary</td></tr><tr><td><code>service</code></td><td>Service name</td><td><code>checkout-api</code></td><td>Deployment unit</td></tr><tr><td><code>environment</code></td><td>Deployment stage</td><td><code>prod</code></td><td>Optional if account-segregated</td></tr><tr><td><code>owner</code></td><td>Team/person</td><td><code>payments-team</code></td><td>Responsibility tracking</td></tr><tr><td><code>cost-center</code></td><td>Cost allocation</td><td><code>payments-team</code></td><td>Billing attribution</td></tr><tr><td><code>backup-required</code></td><td>boolean</td><td><code>true</code></td><td>Backup policy enforcement</td></tr><tr><td><code>terraform</code></td><td>boolean</td><td><code>true</code></td><td>IaC management indicator</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="implementation-checklist">Implementation Checklist<a href="https://blog.derrops.com/blog/derrops-naming-sheet#implementation-checklist" class="hash-link" aria-label="Direct link to Implementation Checklist" title="Direct link to Implementation Checklist" translate="no">​</a></h2>
<ul class="contains-task-list containsTaskList_mC6p">
<li class="task-list-item"><input type="checkbox" disabled=""> <strong>Define segments:</strong> org, domain, service values</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <strong>Account strategy:</strong> 1 per environment? (Recommended: yes)</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <strong>Test naming:</strong> Create 1-2 sample resources in non-prod</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <strong>Document exceptions:</strong> Route53 reverse hierarchy, DNS patterns</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <strong>Create IAM policies:</strong> Use path prefixes for least privilege</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <strong>Enable AWS Config:</strong> Enforce naming patterns automatically</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <strong>Set up tagging:</strong> Apply tags to non-nameable resources</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <strong>Create runbooks:</strong> How to find resources by naming convention</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <strong>Train team:</strong> Share cheatsheet and examples</li>
<li class="task-list-item"><input type="checkbox" disabled=""> <strong>Monitor drift:</strong> Regular audits for non-compliant names</li>
</ul>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="format-comparison-examples">Format Comparison Examples<a href="https://blog.derrops.com/blog/derrops-naming-sheet#format-comparison-examples" class="hash-link" aria-label="Direct link to Format Comparison Examples" title="Direct link to Format Comparison Examples" translate="no">​</a></h2>
<table><thead><tr><th>Use Case</th><th>Prefix Hierarchy</th><th>DNS Reverse</th><th>Flat Kebab</th><th>Result</th></tr></thead><tbody><tr><td>Same service across systems</td><td><code>/acme/payments/checkout-api</code></td><td><code>checkout-api.payments.acme.com</code></td><td><code>acme-payments-checkout-api</code></td><td>✅ All represent same logical resource</td></tr><tr><td>Different purposes</td><td><code>/acme/payments/checkout-api/orders</code></td><td>N/A</td><td><code>acme-payments-checkout-api-orders</code></td><td>✅ Additional scope via suffix</td></tr><tr><td>Env segregated</td><td><code>/acme/prod/payments/checkout-api</code></td><td><code>checkout-api.prod.acme.com</code></td><td>❌ Not used (handled via account)</td><td>✅ Account provides namespace</td></tr><tr><td>With partition (logs)</td><td><code>acme/payments/checkout-api/2024/01/15/logs</code></td><td>N/A</td><td>N/A</td><td>✅ Hierarchical querying in S3</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="one-liner-reference">One-Liner Reference<a href="https://blog.derrops.com/blog/derrops-naming-sheet#one-liner-reference" class="hash-link" aria-label="Direct link to One-Liner Reference" title="Direct link to One-Liner Reference" translate="no">​</a></h2>
<p><strong>Need to name a resource?</strong> Apply this logic in order:</p>
<ol>
<li class="">Does it support native hierarchy? → Use it (<code>/</code> for paths, <code>.</code> for DNS)</li>
<li class="">Is it globally unique (S3, ACM, CloudFront)? → Add <code>ap-southeast-2--prod--</code> prefix (literal region and env values)</li>
<li class="">Is it DNS-based? → Use reverse hierarchy: <code>{service}.prod.acme.com</code> (env in domain via account)</li>
<li class="">Otherwise → Use format: <code>{org}-{domain}-{service}-{key}</code> with <code>-</code> delimiters, <code>--</code> between segments</li>
<li class="">Tag everything else that doesn't support naming</li>
</ol>]]></content:encoded>
            <category>devops</category>
            <category>aws</category>
        </item>
        <item>
            <title><![CDATA[Derrops Guide to Config]]></title>
            <link>https://blog.derrops.com/blog/derrops-config</link>
            <guid>https://blog.derrops.com/blog/derrops-config</guid>
            <pubDate>Tue, 24 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[This document defines where configuration and secrets live, why,]]></description>
            <content:encoded><![CDATA[<p>This document defines <strong>where configuration and secrets live</strong>, <strong>why</strong>,
and <strong>the rules governing each layer</strong>.<br>
<!-- -->The goal is to make configuration <strong>explicit, auditable, secure, and
predictable</strong>.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="where-does-configuration-live">Where does configuration live?<a href="https://blog.derrops.com/blog/derrops-config#where-does-configuration-live" class="hash-link" aria-label="Direct link to Where does configuration live?" title="Direct link to Where does configuration live?" translate="no">​</a></h2>
<p>Configuration is split into <strong>layers</strong>, each with a <strong>clear
responsibility</strong>.<br>
<!-- -->No single system should answer every question.</p>
<h1>Configuration Authority Hierarchy</h1>
<p>Each configuration value has exactly one authority.</p>
<table><thead><tr><th>Authority</th><th>System / Layer</th><th>Responsibility</th><th>Example</th></tr></thead><tbody><tr><td><strong>Local Authority</strong></td><td>Env File (.env file)</td><td>Local dev enablement</td><td>Allowed regions, schemas</td></tr><tr><td><strong>Policy Authority</strong></td><td>Git (repository)</td><td>What is allowed</td><td>Allowed regions, schemas</td></tr><tr><td><strong>Runtime Authority</strong></td><td>SSM Parameter Store</td><td>Defines what is active</td><td>Feature flags, live endpoints</td></tr><tr><td><strong>Secret Authority</strong></td><td>Secrets Manager</td><td>Defines secret values</td><td>API keys, passwords</td></tr><tr><td><strong>Sensitive Config Authority</strong></td><td>SecureString</td><td>Manages sensitive config</td><td>Encrypted DB URI</td></tr><tr><td><strong>Validation Authority</strong></td><td>Code schema validation</td><td>Ensures correctness</td><td>Zod/Valibot/Yup schema checks</td></tr><tr><td><strong>Consumption Authority</strong></td><td>Application code</td><td>Consumes config</td><td>Reads config object in code</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="env-files-local-development-layer">.env files (Local Development Layer)<a href="https://blog.derrops.com/blog/derrops-config#env-files-local-development-layer" class="hash-link" aria-label="Direct link to .env files (Local Development Layer)" title="Direct link to .env files (Local Development Layer)" translate="no">​</a></h2>
<p><code>.env</code> files are used only for local development.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="purpose">Purpose<a href="https://blog.derrops.com/blog/derrops-config#purpose" class="hash-link" aria-label="Direct link to Purpose" title="Direct link to Purpose" translate="no">​</a></h2>
<ul>
<li class="">Provide secrets and configuration for local execution</li>
<li class="">Allow developers to run services without cloud dependency</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="rules">Rules<a href="https://blog.derrops.com/blog/derrops-config#rules" class="hash-link" aria-label="Direct link to Rules" title="Direct link to Rules" translate="no">​</a></h2>
<ul>
<li class="">Must not be committed to Git</li>
<li class="">Must be gitignored</li>
<li class="">Must only exist locally</li>
<li class="">Must not be used in production</li>
<li class="">Must follow the same schema as production configuration</li>
</ul>
<hr>
<h1>Git (Policy Layer)</h1>
<p><strong>Git is the source of truth for intent and policy.</strong></p>
<p>It answers <em>why</em> something exists, not <em>what it currently is</em>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="git-defines">Git defines<a href="https://blog.derrops.com/blog/derrops-config#git-defines" class="hash-link" aria-label="Direct link to Git defines" title="Direct link to Git defines" translate="no">​</a></h2>
<ul>
<li class="">Allowed values</li>
<li class="">Schemas</li>
<li class="">Constraints</li>
<li class="">Policy-level defaults</li>
<li class="">Environment shapes</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="git-must-not-contain">Git must not contain<a href="https://blog.derrops.com/blog/derrops-config#git-must-not-contain" class="hash-link" aria-label="Direct link to Git must not contain" title="Direct link to Git must not contain" translate="no">​</a></h2>
<ul>
<li class="">Secrets</li>
<li class="">Runtime values</li>
<li class="">Environment-specific endpoints</li>
<li class="">Credentials</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="rules-1">Rules<a href="https://blog.derrops.com/blog/derrops-config#rules-1" class="hash-link" aria-label="Direct link to Rules" title="Direct link to Rules" translate="no">​</a></h2>
<ul>
<li class="">All changes require review</li>
<li class="">All changes must be auditable</li>
<li class="">Git defines policy, not runtime state</li>
</ul>
<hr>
<h1>SSM Parameter Store (Runtime Configuration Layer)</h1>
<p><strong>SSM represents runtime configuration state.</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="rules-2">Rules<a href="https://blog.derrops.com/blog/derrops-config#rules-2" class="hash-link" aria-label="Direct link to Rules" title="Direct link to Rules" translate="no">​</a></h2>
<ul>
<li class="">Values must conform to Git policy</li>
<li class="">Values must be environment-scoped</li>
<li class="">Changes must not require rebuilds</li>
<li class="">Parameters must be hierarchical and namespaced</li>
<li class="">Secrets Manager should be preferred over SSM for secrets</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="naming-convention">Naming Convention<a href="https://blog.derrops.com/blog/derrops-config#naming-convention" class="hash-link" aria-label="Direct link to Naming Convention" title="Direct link to Naming Convention" translate="no">​</a></h2>
<p><code>/{org}/{system}/{env}/{service}/{key}</code></p>
<p>Example:</p>
<p>/fortiro/protect/prod/api/feature/scan-enabled
/fortiro/protect/prod/api/db/host</p>
<hr>
<h1>Secrets Manager (Primary Secrets Layer)</h1>
<p><strong>Secrets Manager is the preferred system for managing secrets.</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="rules-3">Rules<a href="https://blog.derrops.com/blog/derrops-config#rules-3" class="hash-link" aria-label="Direct link to Rules" title="Direct link to Rules" translate="no">​</a></h2>
<ul>
<li class="">Must be injected at runtime</li>
<li class="">Must never be hardcoded</li>
<li class="">Must never be committed to Git</li>
<li class="">Must be readable only by authorised services</li>
<li class="">Rotation should be enabled when possible</li>
</ul>
<hr>
<h1>Tags (Metadata Layer)</h1>
<p>Tags define ownership and metadata.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="rules-4">Rules<a href="https://blog.derrops.com/blog/derrops-config#rules-4" class="hash-link" aria-label="Direct link to Rules" title="Direct link to Rules" translate="no">​</a></h2>
<ul>
<li class="">All AWS resources must be tagged</li>
<li class="">Tags must never control application behavior</li>
<li class="">Tags must not contain secrets</li>
</ul>
<hr>
<h1>Codebase (Consumer Layer)</h1>
<p>The codebase consumes configuration.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="rules-5">Rules<a href="https://blog.derrops.com/blog/derrops-config#rules-5" class="hash-link" aria-label="Direct link to Rules" title="Direct link to Rules" translate="no">​</a></h2>
<ul>
<li class="">Must not define runtime values</li>
<li class="">Must not define secrets</li>
<li class="">Must validate schema</li>
<li class="">Must fail fast on missing configuration</li>
</ul>
<hr>
<h1>Fail Fast Requirement</h1>
<p>Application startup must fail if:</p>
<ul>
<li class="">Required configuration is missing</li>
<li class="">Secrets cannot be retrieved</li>
<li class="">Configuration violates schema</li>
</ul>
<hr>
<h1>Configuration Injection Model</h1>
<p>Configuration must be injected at runtime via:</p>
<ul>
<li class="">Environment variables</li>
<li class="">SSM Parameter Store</li>
<li class="">Secrets Manager</li>
</ul>
<p>Configuration must never be compiled into the application artifact.</p>
<hr>
<h1>Runtime Defaults Policy</h1>
<p>Defaults must exist only in configuration, not application logic.</p>
<p>Allowed:</p>
<p>size: params.size ?? config['app.pagination.default.size']</p>
<p>Forbidden:</p>
<p>size: params.size ?? 20</p>
<hr>
<h1>Environment Precedence</h1>
<p>Highest precedence first:</p>
<ol>
<li class="">Explicit test configuration injection</li>
<li class="">System environment variables</li>
<li class="">Local <code>.env</code></li>
<li class="">SSM Parameter Store</li>
<li class="">Secrets Manager</li>
<li class="">Git policy</li>
<li class="">Code schema validation</li>
</ol>
<hr>
<h1>Result</h1>
<p>This model ensures:</p>
<ul>
<li class="">Secure secret handling</li>
<li class="">Explicit runtime configuration</li>
<li class="">Fail-fast startup guarantees</li>
<li class="">Predictable deployments</li>
</ul>
<h1>Glossary</h1>
<ul>
<li class="">Deployment Instances</li>
</ul>]]></content:encoded>
            <category>Typescript</category>
            <category>devops</category>
            <category>aws</category>
            <category>config</category>
        </item>
        <item>
            <title><![CDATA[Good Optimization is Never Premature]]></title>
            <link>https://blog.derrops.com/blog/good-optimization</link>
            <guid>https://blog.derrops.com/blog/good-optimization</guid>
            <pubDate>Thu, 30 Oct 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[“Premature optimization is the root of all evil.”]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>“Premature optimization is the root of all evil.”
— <cite>Donald Knuth</cite></p>
</blockquote>
<p>But as somebody who studied optimization and mathematics in university, optimization is literally one of my favourite topics.</p>
<p>My first job after graduating was working at a company which designed and manufactured CNC tool grinding machines as well as developing the software. I worked in the Application Software Engineering team. One component of the software was called the <strong>Contact Solver</strong>. This component was responsible for calculating when the grinding wheel would make contact with the geometry of the tool.</p>
<p><img decoding="async" loading="lazy" alt="tool.png" src="https://blog.derrops.com/assets/images/tool-4ffa0d68332456a850d3238252d1f5f2.png" width="1536" height="1322" class="img_ev3q"></p>
<p>The actual real mathematical calculation of this was extremely complicated, as the surface of drill bits, mills etc can be highly complex. The mathematics is very complex, even with my background in Applied Mathematics at University I couldn't do a lot of the true 3D Calculations 😱</p>
<table><thead><tr><th><strong>Contact Type</strong></th><th><strong>Formula</strong></th></tr></thead><tbody><tr><td><strong>Sphere–Sphere</strong></td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>contact</mtext><mtext>  </mtext><mo>⟺</mo><mtext>  </mtext><mi mathvariant="normal">∥</mi><msub><mi>c</mi><mn>2</mn></msub><mo>−</mo><msub><mi>c</mi><mn>1</mn></msub><mi mathvariant="normal">∥</mi><mo>≤</mo><msub><mi>r</mi><mn>1</mn></msub><mo>+</mo><msub><mi>r</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">\text{contact} \iff \|c_2 - c_1\| \le r_1 + r_2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6391em;vertical-align:-0.024em"></span><span class="mord text"><span class="mord">contact</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">⟺</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord">∥</span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mord">∥</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.7333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span></td></tr><tr><td><strong>Sphere–AABB</strong></td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi><mo>=</mo><mi mathvariant="normal">clamp</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>c</mi><mo separator="true">,</mo><mi>m</mi><mo separator="true">,</mo><mi>M</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mspace width="1em"></mspace><mtext>contact</mtext><mtext>  </mtext><mo>⟺</mo><mtext>  </mtext><mi mathvariant="normal">∥</mi><mi>p</mi><mo>−</mo><mi>c</mi><mi mathvariant="normal">∥</mi><mo>≤</mo><mi>r</mi></mrow><annotation encoding="application/x-tex">p = \operatorname{clamp}(c, m, M), \quad \text{contact} \iff \|p - c\| \le r</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em"></span><span class="mord mathnormal">p</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mop"><span class="mord mathrm">clamp</span></span><span class="mopen">(</span><span class="mord mathnormal">c</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal">m</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord mathnormal" style="margin-right:0.10903em">M</span><span class="mclose">)</span><span class="mpunct">,</span><span class="mspace" style="margin-right:1em"></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord text"><span class="mord">contact</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">⟺</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord">∥</span><span class="mord mathnormal">p</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal">c</span><span class="mord">∥</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.4306em"></span><span class="mord mathnormal" style="margin-right:0.02778em">r</span></span></span></span></td></tr><tr><td><strong>Capsule–Capsule</strong></td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi><mo>=</mo><mi mathvariant="normal">dist</mi><mo>⁡</mo><mo stretchy="false">(</mo><mo stretchy="false">[</mo><msub><mi>a</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>b</mi><mn>1</mn></msub><mo stretchy="false">]</mo><mo separator="true">,</mo><mo stretchy="false">[</mo><msub><mi>a</mi><mn>2</mn></msub><mo separator="true">,</mo><msub><mi>b</mi><mn>2</mn></msub><mo stretchy="false">]</mo><mo stretchy="false">)</mo><mo separator="true">,</mo><mspace width="1em"></mspace><mtext>contact</mtext><mtext>  </mtext><mo>⟺</mo><mtext>  </mtext><mi>d</mi><mo>≤</mo><msub><mi>r</mi><mn>1</mn></msub><mo>+</mo><msub><mi>r</mi><mn>2</mn></msub></mrow><annotation encoding="application/x-tex">d = \operatorname{dist}([a_1, b_1], [a_2, b_2]), \quad \text{contact} \iff d \le r_1 + r_2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em"></span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mop"><span class="mord mathrm">dist</span></span><span class="mopen">([</span><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">]</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mopen">[</span><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">])</span><span class="mpunct">,</span><span class="mspace" style="margin-right:1em"></span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord text"><span class="mord">contact</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">⟺</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8304em;vertical-align:-0.136em"></span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.7333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.02778em">r</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:-0.0278em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span></td></tr><tr><td><strong>Triangle–Triangle / Primitive</strong></td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>contact</mtext><mtext>  </mtext><mo>⟺</mo><mtext>  </mtext><msub><mi>d</mi><mi>min</mi><mo>⁡</mo></msub><mo>≤</mo><mn>0</mn><mspace width="1em"></mspace><mo stretchy="false">(</mo><mtext>or&nbsp;</mtext><msub><mi>d</mi><mi>min</mi><mo>⁡</mo></msub><mo>≤</mo><mi>ε</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\text{contact} \iff d_{\min} \le 0 \quad (\text{or } d_{\min} \le \varepsilon)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6391em;vertical-align:-0.024em"></span><span class="mord text"><span class="mord">contact</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">⟺</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3175em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mop mtight"><span class="mtight">m</span><span class="mtight">i</span><span class="mtight">n</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord">0</span><span class="mspace" style="margin-right:1em"></span><span class="mopen">(</span><span class="mord text"><span class="mord">or&nbsp;</span></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3175em"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mop mtight"><span class="mtight">m</span><span class="mtight">i</span><span class="mtight">n</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord mathnormal">ε</span><span class="mclose">)</span></span></span></span></td></tr><tr><td><strong>Separating Axis Theorem (SAT)</strong></td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi>max</mi><mo>⁡</mo></mrow><mrow><mi>a</mi><mo>∈</mo><mi>A</mi></mrow></msub><mo stretchy="false">⟨</mo><mi>a</mi><mo separator="true">,</mo><mover accent="true"><mi>n</mi><mo>^</mo></mover><mo stretchy="false">⟩</mo><mo>&lt;</mo><msub><mrow><mi>min</mi><mo>⁡</mo></mrow><mrow><mi>b</mi><mo>∈</mo><mi>B</mi></mrow></msub><mo stretchy="false">⟨</mo><mi>b</mi><mo separator="true">,</mo><mover accent="true"><mi>n</mi><mo>^</mo></mover><mo stretchy="false">⟩</mo></mrow><annotation encoding="application/x-tex">\max_{a \in A} \langle a, \hat{n} \rangle &lt; \min_{b \in B} \langle b, \hat{n} \rangle</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mop"><span class="mop">max</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3283em"><span style="top:-2.55em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">a</span><span class="mrel mtight">∈</span><span class="mord mathnormal mtight">A</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1774em"><span></span></span></span></span></span></span><span class="mopen">⟨</span><span class="mord mathnormal">a</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">n</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.25em"><span class="mord">^</span></span></span></span></span></span></span><span class="mclose">⟩</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">&lt;</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mop"><span class="mop">min</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">b</span><span class="mrel mtight">∈</span><span class="mord mathnormal mtight" style="margin-right:0.05017em">B</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.1774em"><span></span></span></span></span></span></span><span class="mopen">⟨</span><span class="mord mathnormal">b</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em"></span><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6944em"><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="mord mathnormal">n</span></span><span style="top:-3em"><span class="pstrut" style="height:3em"></span><span class="accent-body" style="left:-0.25em"><span class="mord">^</span></span></span></span></span></span></span><span class="mclose">⟩</span></span></span></span>; <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>contact</mtext><mtext>  </mtext><mo>⟺</mo><mtext>  </mtext><mtext>no&nbsp;separating&nbsp;axis&nbsp;exists</mtext></mrow><annotation encoding="application/x-tex">\text{contact} \iff \text{no separating axis exists}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6391em;vertical-align:-0.024em"></span><span class="mord text"><span class="mord">contact</span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">⟺</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8623em;vertical-align:-0.1944em"></span><span class="mord text"><span class="mord">no&nbsp;separating&nbsp;axis&nbsp;exists</span></span></span></span></span></td></tr><tr><td>...</td><td>Many more complicated formulas</td></tr></tbody></table>
<p>One way I was in my manager manager's office at the time, and he complained that the Contact Solver was terrible for long tools, and solutions were taking too long.</p>
<blockquote>
<blockquote>
<p>Challenge Accepted 🦸‍♂️!</p>
</blockquote>
</blockquote>
<p>At this time in my life this was pre-children, and I used to have plenty of time to play games on PC, so I was into gaming, I even had spare money for a gaming Laptop! I had what I would say a vague interest in the gaming industry, but everything I heard about it sounded actually terrible to work in job-wise.</p>
<p>But I did take somewhat of an interest in Game engines, and had played around with running some on my machine myself. Game engines I would describe now in my world are opinionated frameworks that solve a problem common problems in the domain of making games:</p>
<table><thead><tr><th>Domain</th><th>Example Problems Solved</th></tr></thead><tbody><tr><td><strong>Physics</strong></td><td>Collision, contact solving, dynamics</td></tr><tr><td><strong>Rendering</strong></td><td>Lighting, shaders, culling</td></tr><tr><td><strong>Gameplay</strong></td><td>ECS, AI, time, state</td></tr><tr><td><strong>Audio</strong></td><td>Spatial sound, effects, streaming</td></tr><tr><td><strong>Assets</strong></td><td>Importing, streaming, memory</td></tr><tr><td><strong>Networking</strong></td><td>Sync, prediction, replay</td></tr><tr><td><strong>Tooling</strong></td><td>Editors, profilers, visual scripting</td></tr><tr><td><strong>Infrastructure</strong></td><td>Multithreading, serialization, localization</td></tr></tbody></table>
<p>And I was interested in the <strong>Physics</strong> part, having a background in mathematics at university. Whilst the mathematical problem of asking:</p>
<blockquote>
<p>Is there is contact between two objects? Or When do they intersect?</p>
</blockquote>
<p>Is a very <strong>complex</strong> one. An easier question can be to ask:</p>
<blockquote>
<p>Are these two objects <strong>not</strong> in contact?</p>
</blockquote>
<p>Because that question can be easier to ask. I thought back to my high-level knowledge of game engines:</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="game-engine-optimizations">Game Engine Optimizations<a href="https://blog.derrops.com/blog/good-optimization#game-engine-optimizations" class="hash-link" aria-label="Direct link to Game Engine Optimizations" title="Direct link to Game Engine Optimizations" translate="no">​</a></h2>
<ol>
<li class="">
<p>Firstly I knew in a game engine, that the first optimization game engines make are to break the map down into regions. When two object are not in the same region, they do not need to be checked if they are colliding.</p>
</li>
<li class="">
<p>Secondly, I knew that all game engines approximate shapes usually as <strong>polygons</strong>. Which can be simpler to detect collisions than on the real geometry. I also knew that more than likely these geometries were further approximated for this purpose.
<img decoding="async" loading="lazy" alt="polygons.png" src="https://blog.derrops.com/assets/images/polygons-1807b0b4e2d19e9fb2ce27d2dd771934.png" width="2634" height="910" class="img_ev3q"></p>
</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="applying-these-optimizations">Applying these Optimizations<a href="https://blog.derrops.com/blog/good-optimization#applying-these-optimizations" class="hash-link" aria-label="Direct link to Applying these Optimizations" title="Direct link to Applying these Optimizations" translate="no">​</a></h2>
<p>I then thought I will try and take some of these optimizations myself back into our solution. I hoped this would make things much faster. We had this sandbox application which showed the grinding wheel contacting the tool. It had a scroller which when moved, calculated where the wheel should be, and moved it there. Long tools in particular were extremely laggy, just like those video games I used to play when you had too much stuff in the scene.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="approach">Approach<a href="https://blog.derrops.com/blog/good-optimization#approach" class="hash-link" aria-label="Direct link to Approach" title="Direct link to Approach" translate="no">​</a></h2>
<p>Although we did have polygon surfaces when we rendered the tool and our grinding wheel in 3D in our application, I thought to myself...</p>
<blockquote>
<p>Hey, both the tool, and the wheel could be put in inside a <strong>Bounding Cylinder</strong> instead of a <strong>Bounding Box</strong> or <strong>Bounding Polygon</strong></p>
</blockquote>
<p><img decoding="async" loading="lazy" alt="bounded-tool" src="https://blog.derrops.com/assets/images/bounded-tool-135301c4db7ec8bae73fd9f869d60788.png" width="1118" height="244" class="img_ev3q"></p>
<p>Then I could ask the question: "Do these two cylinders make contact?</p>
<p>So the real algorithm becomes:</p>
<ul>
<li class="">Calculate Bounding Cylinders of wheel and Tool</li>
<li class="">Check if they make contact?</li>
<li class="">If yes then continue with complex contact solving</li>
<li class="">If no then they don't make contact and <code>false</code> can be returned</li>
</ul>
<!-- -->
<p>I really liked this strategy as this was such a core important component, I could make very conservative optimizations that were more like sanity checks that these objects are not in contact. I used some Object Orientated techniques and added a method for each fundamental geometric object to calculate it's bounding cylinder. For examples we had an object which was a 2D surface rotated around an axis. I could calculate the largest radius, and the bound complex cylindrical geometries, which could then feed through the bounding cylinder check.</p>
<p>My final result had the tool divided up into many different cylindrical sections stitched together, to further minimize the bounding geometry to achieve what I regarded as the perfect balance between optimization and accuracy. The cylinders were a great geometry for these types of tools, with one cylinder potentially giving far more accuracy than millions of polygons.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="summary">Summary<a href="https://blog.derrops.com/blog/good-optimization#summary" class="hash-link" aria-label="Direct link to Summary" title="Direct link to Summary" translate="no">​</a></h2>
<p>The results were fantastic! In our sandbox application, the wheel smoothly moved through the simulation. There was virtually no latency, even for very long tools. This taught me a valuable lesson.</p>
<ol>
<li class="">That simple optimizations in highly complex calculations can yield extreme performance gains</li>
<li class="">Interest in random technology can give you great ideas</li>
</ol>]]></content:encoded>
            <category>optimization</category>
            <category>performance</category>
            <category>mathematics</category>
            <category>physics</category>
            <category>simulation</category>
            <category>software-engineering</category>
            <category>cnc</category>
            <category>game-engines</category>
        </item>
        <item>
            <title><![CDATA[Chain Reaction or a Real Life Lesson in Reliability Engineering at NEMO]]></title>
            <link>https://blog.derrops.com/blog/NEMO-chain-reaction</link>
            <guid>https://blog.derrops.com/blog/NEMO-chain-reaction</guid>
            <pubDate>Sun, 19 Oct 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[I recently visited Amsterdam with my family. At Amsterdam there is a science museum called NEMO. It's specifically for children to learn about science. There were all sorts of experiments and contraptions the kids could interact with, from electricity, to sound, motion, pulleys and more.]]></description>
            <content:encoded><![CDATA[<p>I recently visited Amsterdam with my family. At Amsterdam there is a science museum called NEMO. It's specifically for children to learn about science. There were all sorts of experiments and contraptions the kids could interact with, from electricity, to sound, motion, pulleys and more.</p>
<p>But one of the exhibitions caught my eye: <strong>The Chain Reaction</strong> <img decoding="async" loading="lazy" alt="NEMO-chain-reaction" src="https://blog.derrops.com/assets/images/nemo-chain-reaction-c0a723049a65740b8e2dff7e6955ceb1.png" width="1300" height="955" class="img_ev3q"></p>
<p>The "Chain Reaction" is a scheduled live demonstration/show at NEMO that explores the principles of cause &amp; effect, and potential and kinetic energy.</p>
<ul>
<li class="">It features large-scale contraptions and setups (dominoes, rolling chairs, seesaws, flying cars, etc) showing how one action triggers the next.
Wikipedia</li>
</ul>
<p>This exhibition took me back to the 90s as a kid, playing the old game: <strong>Incredible Machine</strong>:</p>
<p><img decoding="async" loading="lazy" alt="the-incredible-machine" src="https://blog.derrops.com/assets/images/the-incredible-machine-a64aadac9a8d70ad469999b4ed35b6c9.png" width="439" height="512" class="img_ev3q"></p>
<p>This is a classic contraption puzzle where things like a hamster/mouse wheel, ropes, pulleys, fans, etc. trigger chain reactions. We built all sorts of funny contraptions just like the one at NEMO as a kid, we could play this for hours, everybody tried to incorporate everything they could into their contraptions, it was amazing fun.</p>
<p>Through the eyes of a child, the chain reaction is very captivating, the kids try and imagine just how this thing is going to well, and how <em>Incredible</em> it will be.</p>
<p>But unfortunately for me I'm no longer a kid, and when I looked at this contraption, I saw this thing through the eyes of an engineer, a <strong>DevOps</strong> engineer. I know from real-life experience these sorts of contraptions, are not ideal from an operations support POV. The issue lies in the fact that if anyone of these sub-systems breaks, then the whole reaction will no longer work. In real life, real physical systems do not behave like they do on the computer. In the <em>Incredible Machine</em>, when you ran your reaction, the behavior was exactly the same each time, but in real-life this is rarely the case.</p>
<p>The whole exhibition was one long Series Chain System Structure:</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="system-structure">System Structure<a href="https://blog.derrops.com/blog/NEMO-chain-reaction#system-structure" class="hash-link" aria-label="Direct link to System Structure" title="Direct link to System Structure" translate="no">​</a></h2>
<table><thead><tr><th><strong>System Structure</strong></th><th><strong>Formula</strong></th><th><strong>Effect</strong></th><th><strong>Engineering Example</strong></th></tr></thead><tbody><tr><td><strong>Series (Chain)</strong></td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>R</mi><mo>=</mo><msub><mi>R</mi><mn>1</mn></msub><mo>×</mo><msub><mi>R</mi><mn>2</mn></msub><mo>×</mo><mo>⋯</mo><mo>×</mo><msub><mi>R</mi><mi>n</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(R = R_1 \times R_2 \times \dots \times R_n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.00773em">R</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.00773em">R</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:-0.0077em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.00773em">R</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em"><span style="top:-2.55em;margin-left:-0.0077em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:0.6667em;vertical-align:-0.0833em"></span><span class="minner">⋯</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.00773em">R</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em"><span style="top:-2.55em;margin-left:-0.0077em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span></td><td>Reliability decreases as chain grows</td><td>API call chain (Auth → Service A → Service B → DB)</td></tr><tr><td><strong>Parallel (Redundant)</strong></td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>R</mi><mo>=</mo><mn>1</mn><mo>−</mo><mo>∏</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><msub><mi>R</mi><mi>i</mi></msub><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(R = 1 - \prod(1 - R_i))</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord mathnormal" style="margin-right:0.00773em">R</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mop op-symbol small-op" style="position:relative;top:0em">∏</span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.00773em">R</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:-0.0077em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">))</span></span></span></span></td><td>Reliability increases with redundancy</td><td>Load-balanced servers, RAID storage</td></tr><tr><td><strong>Hybrid</strong></td><td>Mix of both</td><td>Balance of fragility and resilience</td><td>Microservices with retries, caches, circuit breakers</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="game-time">Game time<a href="https://blog.derrops.com/blog/NEMO-chain-reaction#game-time" class="hash-link" aria-label="Direct link to Game time" title="Direct link to Game time" translate="no">​</a></h2>
<p>I placed my bets! I told my wife I predict this thing won't work flawlessly, that something will break. Over time each of these systems may be slowly becoming less reliable. But lets say for argument sake there are 100 sub-systems, and each one fails every 200 attempts. That means:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mfrac><mn>1</mn><mn>1000</mn></mfrac><msup><mo stretchy="false">)</mo><mn>200</mn></msup><mo>=</mo><mn>0.606</mn></mrow><annotation encoding="application/x-tex">(1 - \frac{1}{1000})^{200} = 0.606</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1.1901em;vertical-align:-0.345em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8451em"><span style="top:-2.655em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1000</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.394em"><span class="pstrut" style="height:3em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mclose"><span class="mclose">)</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em"><span style="top:-3.063em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">200</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:0.6444em"></span><span class="mord">0.606</span></span></span></span></p>
<p>Which means a <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>60.6</mn><mi mathvariant="normal">%</mi></mrow><annotation encoding="application/x-tex">60.6\%</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8056em;vertical-align:-0.0556em"></span><span class="mord">60.6%</span></span></span></span> chance of success. But hey, I'm pessimistic.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="chain-reaction-start">Chain Reaction Start<a href="https://blog.derrops.com/blog/NEMO-chain-reaction#chain-reaction-start" class="hash-link" aria-label="Direct link to Chain Reaction Start" title="Direct link to Chain Reaction Start" translate="no">​</a></h2>
<p>The host searched for a volunteer child, there were no shortages. One girl was eventually selected. She was given a helmet👷‍♀️ to wear (safety first) given way too mich instructions for a small task but anyway eventually she started the reaction. And off went all the contraptions, a flame 🔥 turns on, and cuts a rope , a toy 🚗 crashes into something, a 🎱 ball rolls down something etc. I honestly couldn't recite anything near the actual order, it's overwhelming all the different objects and motions.</p>
<p>But eventually the experiment got to a hammer, which swang down to cut a rope of some sort of suspended mass so it could fall. But instead of the rope allowing the object overhead to free fall, it gets caught on the hammer 🔨 wrapping around it, stopping the experiment.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="missed-opportunity-for-learning">Missed Opportunity for Learning<a href="https://blog.derrops.com/blog/NEMO-chain-reaction#missed-opportunity-for-learning" class="hash-link" aria-label="Direct link to Missed Opportunity for Learning" title="Direct link to Missed Opportunity for Learning" translate="no">​</a></h2>
<p>When things go wrong this is the best learning I've found. Things going right in tech don't really teach you anything. Everything works till it doesn't. Yes sure there was a fault in the reaction, and our instinct is to jump on to the issue and fix it. Just like bugs 🐞 is Software. But often a bug should not disrupt an entire process/transaction. It's sometimes better to ask why any sort of problem, is so decremental to the system, and how it could be made more resilient as a whole. And unfortunately the reliability of this system is not really anything to do with one bug, but more the structure of the system.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-to-mitigate">How to Mitigate<a href="https://blog.derrops.com/blog/NEMO-chain-reaction#how-to-mitigate" class="hash-link" aria-label="Direct link to How to Mitigate" title="Direct link to How to Mitigate" translate="no">​</a></h2>
<p>We are always constrained when developing technical solutions, whilst running everything with a redundant parallel experiment might theoretically seem like a good idea, it's not really an option. Instead the technician provided redundancy, manually starting the next link. There are other patterns though which may be applicable, such as retry, which reminds me of a Queue-Consumer scenario. As I write this I see that I am worried I am seeing the world in Software Components 😕.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="where-to-from-here">Where to from here<a href="https://blog.derrops.com/blog/NEMO-chain-reaction#where-to-from-here" class="hash-link" aria-label="Direct link to Where to from here" title="Direct link to Where to from here" translate="no">​</a></h3>
<p>In the future I predict we will start to see the structure of these solutions in the tech world driven by LLMs. What is meant by <em>Reliability</em> will be more and more hard to define, and will be disconnected from the logical flow of code, or the cause and effect in a chain reaction not too unlike that of what was at NEMO.</p>]]></content:encoded>
            <category>reliability-engineering</category>
            <category>nemo</category>
        </item>
    </channel>
</rss>