<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Kai Siren's Blog]]></title><description><![CDATA[⚙️ ( DevOps || Platform || Infrastructure ) && Engineer ⚙️]]></description><link>https://coilysiren.me</link><generator>GatsbyJS</generator><lastBuildDate>Tue, 14 Apr 2026 07:28:06 GMT</lastBuildDate><author><![CDATA[@coilysiren]]></author><item><title><![CDATA[3 Cloud Standoff: IAM Across AWS, GCP, and Azure]]></title><description><![CDATA[A practical mapping of identity fundamentals across AWS, GCP, and Azure. What the words mean in each cloud, and where the mental models diverge.]]></description><link>https://coilysiren.me/posts/3-cloud-standoff/</link><guid isPermaLink="false">https://coilysiren.me/posts/3-cloud-standoff/</guid><dc:creator><![CDATA[@coilysiren]]></dc:creator><pubDate>Mon, 13 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The hardest part of moving between clouds isn&apos;t the compute or the networking. It&apos;s figuring out what &quot;identity&quot; even means in a given ecosystem. Maybe your company acquired something that runs on GCP. Maybe the data science team got a budget for Azure OpenAI. Maybe you&apos;re just tired of AWS. Either way, you have to relearn IAM from scratch, and the vocabulary is a minefield.&lt;/p&gt;
&lt;p&gt;This post maps the three clouds against each other. I&apos;ll walk through each cloud&apos;s identity fundamentals, then line them up adjacent to each other, and finally call out the places where the mental models genuinely diverge (as opposed to just using different words for the same thing).&lt;/p&gt;
&lt;p&gt;This post assumes you already understand IAM in at least one cloud. If you don&apos;t, start with &lt;a href=&quot;https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html&quot;&gt;the AWS IAM docs&lt;/a&gt;. They&apos;re the most thorough and the concepts transfer reasonably well.&lt;/p&gt;
&lt;h2&gt;The fundamentals, by cloud&lt;/h2&gt;
&lt;h3&gt;AWS&lt;/h3&gt;
&lt;p&gt;AWS has the oldest and most-copied identity model, so it&apos;s a reasonable starting point.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;IAM User&lt;/strong&gt;. A long-lived identity, usually with an access key ID and secret access key. Meant for humans before SSO existed, and for machines that live outside AWS. Today, best practice is to have as few of these as possible.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IAM Role&lt;/strong&gt;. An identity with &lt;em&gt;no&lt;/em&gt; long-lived credentials. You &lt;em&gt;assume&lt;/em&gt; a role via STS (&lt;code&gt;sts:AssumeRole&lt;/code&gt;), which hands you back temporary credentials (typically valid for one hour). Roles have a &lt;em&gt;trust policy&lt;/em&gt; that says who&apos;s allowed to assume them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IAM Group&lt;/strong&gt;. A bag of users, used only for attaching policies. Groups are not identities; you can&apos;t &quot;log in as&quot; a group.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Instance Profile&lt;/strong&gt;. A wrapper around a role that lets an EC2 instance use it. The EC2 metadata service (IMDS) hands out temporary credentials based on the attached instance profile.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IAM Policy&lt;/strong&gt;. The permission document itself. Attached to users, groups, roles, or (for some services) to resources directly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IAM Identity Center&lt;/strong&gt; (formerly AWS SSO). The modern way to give humans access. It federates from an external IdP and vends short-lived role sessions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The basic AWS idea: &lt;strong&gt;roles are portable identities that nothing owns&lt;/strong&gt;. An EC2 instance, a Lambda function, a pod in EKS, and a user from another AWS account can all assume the same role.&lt;/p&gt;
&lt;h3&gt;GCP&lt;/h3&gt;
&lt;p&gt;GCP&apos;s identity model looks superficially similar to AWS, but the center of gravity is in a totally different place.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Google Account / Workspace User&lt;/strong&gt;. A human identity, managed entirely outside your GCP project (in Google Workspace or Cloud Identity). You can&apos;t create one &quot;inside&quot; a project the way you create an IAM user in AWS.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service Account&lt;/strong&gt;. GCP&apos;s machine identity. This is the one that trips people up, because a service account is simultaneously:
&lt;ol&gt;
&lt;li&gt;An &lt;em&gt;identity&lt;/em&gt; (it has an email like &lt;code&gt;my-sa@my-project.iam.gserviceaccount.com&lt;/code&gt; and can be granted roles on resources), and&lt;/li&gt;
&lt;li&gt;A &lt;em&gt;resource&lt;/em&gt; (you can be granted roles &lt;em&gt;on&lt;/em&gt; the service account itself, such as &lt;code&gt;roles/iam.serviceAccountTokenCreator&lt;/code&gt; to impersonate it).&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Google Group&lt;/strong&gt;. A bag of users. Same idea as AWS IAM groups, but managed in Workspace rather than in GCP itself.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IAM Role&lt;/strong&gt;. In GCP, a &quot;role&quot; is &lt;em&gt;not&lt;/em&gt; an identity. It&apos;s a collection of permissions (what AWS would call a managed policy). Roles come in three flavors: basic (Owner/Editor/Viewer, avoid these), predefined (per-service, curated by Google), and custom.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IAM Binding / Allow Policy&lt;/strong&gt;. The grant itself. On each resource, there&apos;s an &quot;allow policy&quot; that says &quot;principal X has role Y on this resource.&quot; This is the opposite direction from AWS, where policies are usually attached to the principal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Workload Identity Federation&lt;/strong&gt;. Lets external identities (like a GitHub Actions OIDC token or an AWS IAM role) act as a GCP service account without a long-lived JSON key.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The basic GCP idea: &lt;strong&gt;permissions are granted on resources, not on principals&lt;/strong&gt;. You walk up to a resource and say &quot;who can do what here.&quot; That&apos;s the opposite of AWS, where you walk up to a principal and ask &quot;what can this identity do.&quot;&lt;/p&gt;
&lt;h3&gt;Azure&lt;/h3&gt;
&lt;p&gt;Azure has the most confusing vocabulary of the three, largely because the identity layer (Entra ID, formerly Azure AD) and the resource layer (Azure Resource Manager) were duct taped together later.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;User&lt;/strong&gt;. A human identity in an Entra ID tenant. Managed at the tenant level, not the subscription level.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;App Registration&lt;/strong&gt;. The &lt;em&gt;definition&lt;/em&gt; of an application in Entra ID. It lives in the tenant where the app was created (&quot;home tenant&quot;) and describes the app&apos;s identity, permissions, and secrets. This is just a blueprint, it&apos;s not the thing that actually signs in.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service Principal&lt;/strong&gt;. The &lt;em&gt;instance&lt;/em&gt; of an app registration in a given tenant. The service principal is what actually gets assigned permissions and signs in. If you see the Azure portal distinguish between &quot;App registrations&quot; and &quot;Enterprise applications,&quot; that&apos;s this split: app registrations are definitions, enterprise applications are service principals.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Managed Identity&lt;/strong&gt;. A service principal whose credentials are managed entirely by Azure. You never see a secret. Comes in two flavors: &lt;em&gt;system-assigned&lt;/em&gt; (tied 1:1 to the lifecycle of a single Azure resource) and &lt;em&gt;user-assigned&lt;/em&gt; (a standalone resource you can attach to multiple things).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Azure RBAC Role&lt;/strong&gt;. Like GCP, this is a permission set, not an identity. Comes as built-in roles (curated by Microsoft) or custom roles.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Role Assignment&lt;/strong&gt;. The three-tuple of &lt;code&gt;(principal, role, scope)&lt;/code&gt; that actually grants access. Scope can be management group, subscription, resource group, or individual resource.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The basic Azure idea: &lt;strong&gt;identity and resource access are two separate systems stitched together by role assignments&lt;/strong&gt;. The identity lives in Entra ID; the thing it&apos;s allowed to do lives in Azure RBAC; the role assignment is the bridge.&lt;/p&gt;
&lt;h2&gt;The adjacent view&lt;/h2&gt;
&lt;p&gt;Here&apos;s the side-by-side mapping. Rows are concepts; cells are the closest equivalent in each cloud.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;AWS&lt;/th&gt;
&lt;th&gt;GCP&lt;/th&gt;
&lt;th&gt;Azure&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Human identity&lt;/td&gt;
&lt;td&gt;IAM User &lt;em&gt;or&lt;/em&gt; Identity Center user&lt;/td&gt;
&lt;td&gt;Google / Workspace account&lt;/td&gt;
&lt;td&gt;Entra ID user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grouping of humans&lt;/td&gt;
&lt;td&gt;IAM Group&lt;/td&gt;
&lt;td&gt;Google Group&lt;/td&gt;
&lt;td&gt;Entra ID security group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long-lived machine identity&lt;/td&gt;
&lt;td&gt;IAM User with access keys&lt;/td&gt;
&lt;td&gt;Service Account with JSON key&lt;/td&gt;
&lt;td&gt;Service Principal with client secret or certificate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credential-less workload identity&lt;/td&gt;
&lt;td&gt;IAM Role (via instance profile, IRSA, etc.)&lt;/td&gt;
&lt;td&gt;Service Account attached to a workload&lt;/td&gt;
&lt;td&gt;Managed Identity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Permission set / definition&lt;/td&gt;
&lt;td&gt;Managed or inline IAM policy&lt;/td&gt;
&lt;td&gt;IAM Role (predefined or custom)&lt;/td&gt;
&lt;td&gt;Azure RBAC role definition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Permission grant&lt;/td&gt;
&lt;td&gt;Policy attached to principal (or resource)&lt;/td&gt;
&lt;td&gt;IAM binding on a resource&lt;/td&gt;
&lt;td&gt;Role assignment &lt;code&gt;(principal, role, scope)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&quot;Assume another identity&quot;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sts:AssumeRole&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Service account impersonation&lt;/td&gt;
&lt;td&gt;No direct equivalent (see below)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Federation from external IdP&lt;/td&gt;
&lt;td&gt;IAM Identity Center, SAML, OIDC provider&lt;/td&gt;
&lt;td&gt;Workload Identity Federation&lt;/td&gt;
&lt;td&gt;Entra ID external federation / B2B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Temporary credentials source&lt;/td&gt;
&lt;td&gt;STS&lt;/td&gt;
&lt;td&gt;Metadata server (OAuth2 access tokens)&lt;/td&gt;
&lt;td&gt;IMDS (bearer tokens)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kubernetes workload identity&lt;/td&gt;
&lt;td&gt;IRSA or EKS Pod Identity&lt;/td&gt;
&lt;td&gt;GKE Workload Identity&lt;/td&gt;
&lt;td&gt;Entra Workload Identity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-boundary access&lt;/td&gt;
&lt;td&gt;Role trust policy allowing external account&lt;/td&gt;
&lt;td&gt;Grant role to principal from another project&lt;/td&gt;
&lt;td&gt;Guest users or multi-tenant app registrations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Where the mental models actually diverge&lt;/h2&gt;
&lt;p&gt;The table above makes things look tidier than they are. If you&apos;re going to get tripped up, it&apos;ll be on one of the following.&lt;/p&gt;
&lt;h3&gt;1. Which direction do permissions flow?&lt;/h3&gt;
&lt;p&gt;This is the biggest one.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AWS&lt;/strong&gt;: Policies mostly attach to the &lt;em&gt;principal&lt;/em&gt;. &quot;This user / role can do X, Y, Z across these resources.&quot; Resource-based policies (S3 bucket policies, KMS key policies, etc.) exist as an escape hatch, but the default direction is principal-first.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GCP&lt;/strong&gt;: Bindings attach to the &lt;em&gt;resource&lt;/em&gt;. Every resource has an allow policy listing which principals have which roles. You almost never &quot;look up what a service account can do&quot; as a top-level operation. You look up resources and see who&apos;s on them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Azure&lt;/strong&gt;: Role assignments are a three-tuple stored in the subscription. You can query them by principal, by role, or by scope with roughly equal effort. In practice, most teams think about them resource-first, like GCP.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Why this matters: your mental debugging process is different. In AWS, &quot;why can&apos;t this thing do X?&quot; usually starts with &lt;code&gt;aws iam get-role-policy&lt;/code&gt;. In GCP, it usually starts with &lt;code&gt;gcloud projects get-iam-policy&lt;/code&gt; on the &lt;em&gt;target&lt;/em&gt; resource. In Azure, it&apos;s &lt;code&gt;az role assignment list --assignee &amp;#x3C;principal&gt;&lt;/code&gt; or &lt;code&gt;--scope &amp;#x3C;resource&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;2. Is the machine identity an &quot;it&quot; or a &quot;thing&quot;?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AWS IAM roles&lt;/strong&gt; are abstract. Nothing owns them. Multiple EC2 instances, Lambdas, and EKS pods can all assume the same role simultaneously, and the role doesn&apos;t know or care.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GCP service accounts&lt;/strong&gt; are real resources. They live in a project, they have an email, you can grant IAM roles &lt;em&gt;on&lt;/em&gt; them (like &lt;code&gt;roles/iam.serviceAccountUser&lt;/code&gt; to let someone attach the SA to a VM), and you can impersonate them if you have the right permission. They feel more like &quot;a user that happens to be a robot&quot; than AWS&apos;s &quot;a hat anyone can wear.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Azure managed identities&lt;/strong&gt; sit in the middle. System-assigned managed identities are tied to the lifecycle of exactly one Azure resource. Delete the VM, the identity is gone. User-assigned managed identities are standalone Azure resources that you can attach to multiple things, which makes them closer to AWS roles.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&apos;re coming from AWS and expect to &quot;just assume the role from wherever,&quot; GCP and Azure will feel more constrained. If you&apos;re coming from GCP and expect your identity to have an email you can grant things to, AWS roles will feel invisible in a way that&apos;s hard to get used to.&lt;/p&gt;
&lt;h3&gt;3. Where does &quot;a machine&apos;s identity&quot; actually come from at runtime?&lt;/h3&gt;
&lt;p&gt;All three clouds have a metadata endpoint that hands out short-lived credentials to workloads running on their compute. The mechanism is remarkably similar across all three:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AWS&lt;/strong&gt;: IMDSv2 at &lt;code&gt;169.254.169.254&lt;/code&gt;, returns STS credentials for the attached instance profile.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GCP&lt;/strong&gt;: Metadata server at &lt;code&gt;metadata.google.internal&lt;/code&gt; (also &lt;code&gt;169.254.169.254&lt;/code&gt;), returns OAuth2 access tokens for the attached service account.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Azure&lt;/strong&gt;: IMDS at &lt;code&gt;169.254.169.254&lt;/code&gt;, returns bearer tokens for the attached managed identity.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;They all default to a roughly one-hour token lifetime. They all require the SDK (or your own code) to refresh before expiry. They all fail in subtle ways if the workload&apos;s network egress to &lt;code&gt;169.254.169.254&lt;/code&gt; is blocked, which is a fun thing to remember the first time you put a service mesh in front of a pod.&lt;/p&gt;
&lt;h3&gt;4. Kubernetes workload identity is a zoo&lt;/h3&gt;
&lt;p&gt;If you run Kubernetes on any of these, you&apos;ll eventually need to map a Kubernetes service account (KSA) to a cloud identity, so that pods can call cloud APIs without a mounted secret.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AWS&lt;/strong&gt; has two options that both work. &lt;a href=&quot;https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html&quot;&gt;IRSA&lt;/a&gt; uses an OIDC provider on the EKS cluster; pods get STS tokens via a projected service account token. &lt;a href=&quot;https://docs.aws.amazon.com/eks/latest/userguide/pod-identity.html&quot;&gt;EKS Pod Identity&lt;/a&gt; is the newer mechanism that removes the OIDC provider setup. IRSA is still more widely deployed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GCP&lt;/strong&gt; has &lt;a href=&quot;https://cloud.google.com/kubernetes-engine/docs/concepts/workload-identity&quot;&gt;Workload Identity&lt;/a&gt;, which maps a KSA to a GCP service account via an annotation. Under the hood it&apos;s also OIDC-based, but you don&apos;t manage the provider.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Azure&lt;/strong&gt; has &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview&quot;&gt;Entra Workload Identity&lt;/a&gt;, which replaced the deprecated AAD Pod Identity. Like the others, it&apos;s OIDC-based. AKS publishes an issuer, and the federated credential on an Entra app or managed identity trusts tokens from that issuer bound to a specific KSA.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All three are &quot;the same idea&quot; (projected service account tokens + OIDC federation), but each has its own setup tax. Expect to do some real reading the first time you touch a new one.&lt;/p&gt;
&lt;h3&gt;5. Long-lived credentials: dangerous in different ways&lt;/h3&gt;
&lt;p&gt;Every cloud has a &quot;here be dragons&quot; credential type:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AWS IAM user access keys&lt;/strong&gt; are the oldest of these, and the worst-behaved. They live forever, they&apos;re often committed to git, and they&apos;re the reason &lt;a href=&quot;https://github.com/gitleaks/gitleaks&quot;&gt;gitleaks&lt;/a&gt; exists. Modern AWS guidance is: don&apos;t create them at all. Use Identity Center for humans and roles for machines.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GCP service account JSON keys&lt;/strong&gt; are the same problem in a different wrapper. They look innocuous (just a JSON file!) but they&apos;re long-lived bearer tokens that bypass every &quot;no external access&quot; control you have. &lt;a href=&quot;https://cloud.google.com/iam/docs/service-account-creds&quot;&gt;GCP now disables key creation by default at the org level&lt;/a&gt;, and for good reason. Prefer impersonation or Workload Identity Federation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Azure service principal client secrets&lt;/strong&gt; have the same failure mode. Prefer certificate-based auth or (ideally) managed identities, which never expose a credential in the first place.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The common thread: any time you&apos;re holding a credential that doesn&apos;t have an expiry measured in hours, you&apos;re holding a bomb.&lt;/p&gt;
&lt;h2&gt;A worked example: &quot;give this CI job read access to one bucket&quot;&lt;/h2&gt;
&lt;p&gt;To make the differences concrete, here&apos;s the same task expressed three ways.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AWS&lt;/strong&gt;: create an IAM role with a policy granting &lt;code&gt;s3:GetObject&lt;/code&gt; on &lt;code&gt;arn:aws:s3:::my-bucket/*&lt;/code&gt;, and a trust policy that allows your CI&apos;s OIDC provider (e.g. &lt;code&gt;token.actions.githubusercontent.com&lt;/code&gt;) to assume it for a specific repo/branch. The CI job calls &lt;code&gt;sts:AssumeRoleWithWebIdentity&lt;/code&gt; and gets temporary credentials.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GCP&lt;/strong&gt;: create a Workload Identity Pool and Provider for your CI&apos;s OIDC issuer. Grant the external identity (e.g. a specific GitHub repo) the &lt;code&gt;roles/storage.objectViewer&lt;/code&gt; role directly on the bucket, or have it impersonate a service account that has the role. The CI job exchanges its OIDC token for a GCP access token at the STS endpoint.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Azure&lt;/strong&gt;: create an app registration (or user-assigned managed identity) with a federated credential trusting your CI&apos;s OIDC issuer for a specific repo/branch. Give the resulting service principal the &lt;code&gt;Storage Blob Data Reader&lt;/code&gt; role, scoped to the storage account or container. The CI job exchanges its OIDC token for an Azure access token.&lt;/p&gt;
&lt;p&gt;Notice the shape is identical: OIDC issuer, federation trust, role grant, scoped resource. The words and the order of operations are all different. This is why &quot;I know AWS IAM, how hard can GCP be?&quot; is a trap. The concepts transfer, but the muscle memory doesn&apos;t.&lt;/p&gt;
&lt;h2&gt;Where to go next&lt;/h2&gt;
&lt;p&gt;An adjacent view only gets you to the point of being able to read signs. To actually &lt;em&gt;work&lt;/em&gt; in a second cloud, go read the primary docs for its identity system end to end:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AWS: &lt;a href=&quot;https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html&quot;&gt;IAM User Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;GCP: &lt;a href=&quot;https://cloud.google.com/iam/docs/overview&quot;&gt;IAM overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Azure: &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/role-based-access-control/overview&quot;&gt;Azure RBAC overview&lt;/a&gt; &lt;em&gt;and&lt;/em&gt; &lt;a href=&quot;https://learn.microsoft.com/en-us/entra/fundamentals/whatis&quot;&gt;Entra ID fundamentals&lt;/a&gt;. You need both, because they&apos;re genuinely two separate systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And when you get stuck debugging a permission error in a cloud you don&apos;t live in full-time, remember which direction that cloud flows permissions. Half the &quot;why isn&apos;t this working&quot; problems across clouds come from looking at the wrong end of the grant.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This post was drafted autonomously by Claude (Opus 4.6), then edited by me for tone and voice. The technical claims are mine to stand behind. If you find an error, &lt;a href=&quot;https://github.com/coilysiren&quot;&gt;let me know&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Deploying Azure OpenAI via Terraform]]></title><description><![CDATA[An end to end description of how to get Azure OpenAI deployed via Terraform]]></description><link>https://coilysiren.me/posts/azure-openai-terraform/</link><guid isPermaLink="false">https://coilysiren.me/posts/azure-openai-terraform/</guid><dc:creator><![CDATA[@coilysiren]]></dc:creator><pubDate>Wed, 11 Oct 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This post is aimed at reasonably experienced engineers who are deploying Azure OpenAI for their day job. It assumes you&apos;re familiar with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;cloud deployments in general&lt;/li&gt;
&lt;li&gt;terraform specifically&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As such, this post functions primarily as a reference for the terraform configuration you would need. If you are looking for a more basic understanding of how terraform works, &lt;a href=&quot;https://developer.hashicorp.com/terraform/tutorials&quot;&gt;then Hashicorp has some great tutorials for you!&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;In order to get started here, you need to have already done a few things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.hashicorp.com/terraform/tutorials/state&quot;&gt;Setup terraform locally with the state backend that most fits your needs&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Signed up for an Azure account.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Circa October 2023: a simple personal Azure account will not work here. The Azure OpenAI signup process requires that your Azure account have enterprise support. I would not recommend creating a totally new Azure account as a part of this process.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/microsoft-365/enterprise/subscriptions-licenses-accounts-and-tenants-for-microsoft-cloud-offerings?view=o365-worldwide&quot;&gt;Created a subscription within Azure&lt;/a&gt;. This post assumes you are using a paid subscription, which will need to be setup by someone in your company with billing permissions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/cli/azure/&quot;&gt;Installed the Azure CLI&lt;/a&gt;, &lt;a href=&quot;https://learn.microsoft.com/en-us/cli/azure/authenticate-azure-cli&quot;&gt;and logged into it&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Been granted an Azure role &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles&quot;&gt;like the Contributor role&lt;/a&gt; that allows you perform the relevant API actions within Azure. While it would be ideal to mention the fine grain access control you need to perform these actions, that is out of scope for this blog post. Someone with the &lt;code&gt;Owner&lt;/code&gt; role on your subscription should be able to grant you the &lt;code&gt;Contributor&lt;/code&gt; role.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The above steps will likely require the assistance of your finance and IT teams. Feel free to come back to this post once you&apos;ve finished coordinating with them!&lt;/p&gt;
&lt;p&gt;After setting up all of the above, then the should have all the fundamentals you need to deploy things! Lets go...&lt;/p&gt;
&lt;h2&gt;Security Preface&lt;/h2&gt;
&lt;p&gt;...okay wait.&lt;/p&gt;
&lt;p&gt;Before I mention the terraform itself, I must give an important caveat. The terraform configuration describes here is in its &lt;strong&gt;&lt;em&gt;least secure configuration&lt;/em&gt;&lt;/strong&gt;. Specifically, its in its least secure configuration with regards to network security. If you are following this configuration as-is, then you should only be doing so as a prototype. Essentially you deploying this to prove to your stakeholders, &lt;em&gt;&quot;yes I have the skills required to deploy Azure OpenAI via Terraform&quot;&lt;/em&gt;. You must then follow-up via starting work up the network security improvements.&lt;/p&gt;
&lt;p&gt;Having said that. Lets go...&lt;/p&gt;
&lt;h2&gt;Terraform Configuration&lt;/h2&gt;
&lt;p&gt;...deploy this thing! This blog post presents the configuration as two terraform files. This is for display simplicity, and you really shouldn&apos;t be just stuffing everything into two adjacent files like this. The files have the following folder structure:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# folder structure

terraform/main.tf
terraform/modules/azure-openai/main.tf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here are the files:&lt;/p&gt;
&lt;!-- author note: my queendom for a HCL syntax highlighter... --&gt;
&lt;pre&gt;&lt;code class=&quot;language-HCL&quot;&gt;# file: terraform/main.tf

# Set required versions.
#
# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs
terraform {
  required_providers {
    azurerm = {
      source  = &quot;hashicorp/azurerm&quot;
      version = &quot;~&gt; 3&quot;
    }
  }
}

# Configure your Azure authentication. At my job we use multiple providers with
# the `alias` key to configure prod vs non-prod resources.
#
# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs
provider &quot;azurerm&quot; {
  features {}
}

# Here we init our module which contains all of our actual resources. The keys
# here (eg. stage, vnet_cidr, etc) are all defined in our next file.
#
# docs: https://developer.hashicorp.com/terraform/language/modules
module &quot;azure_openai&quot; {
  source = &quot;modules/azure-openai&quot;

  # eg. prod, staging, dev, etc...
  stage = &quot;prod&quot;

  # Pick somewhere close to you, or close to your customers.
  # Use the following command to get locations:
  #
  #   $ az account list-locations -o table
  location = &quot;westus&quot;

  # The SKU is the pricing tier. I haven&apos;t been able to find a page or command
  # that provides a flat list of all the available SKUs. This page describes some
  # of them, though:
  #
  # https://learn.microsoft.com/en-us/azure/search/search-limits-quotas-capacity
  cognitive_sku = &quot;S0&quot;

  # This configuration is for our network architecture. A complete description
  # of what these numbers mean, and how to set them, is beyond the scope of this
  # post. But I will try my best to describe it in brief!
  #
  # A &quot;vnet&quot; is a virtual network group. This network group needs an address,
  # similar to a street address. The &quot;cidr&quot; is that address, represented as a range.
  # A &quot;subnet&quot; or &quot;sub network&quot; is simply a sub group of the broader vnet.
  # A vnet is like a house. The vnet cdir is the address to that house. The
  # subnets are individual rooms within that house. The subnet cidr is the address
  # for each room. The cidrs are all ranges, so the subnet cidrs are ranges contained
  # within the vnet range. You can use a website like https://cidr.xyz/ to confirm this.
  #
  # ...This was a lot. It deserves its own post...!
  vnet_cidr    = &quot;10.0.0.0/19&quot;
  subnet0_cidr = &quot;10.0.0.0/24&quot;
  subnet1_cidr = &quot;10.0.1.0/24&quot;
  subnet2_cidr = &quot;10.0.10.0/24&quot;
  subnet3_cidr = &quot;10.0.11.0/24&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-HCL&quot;&gt;# file: terraform/modules/azure-openai/main.tf

# Set required versions. The module probably doesn&apos;t need to do this
# when the parent context is already doing it. But it&apos;s here anyway, can&apos;t hurt.
#
# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs
terraform {
  required_providers {
    azurerm = {
      source  = &quot;hashicorp/azurerm&quot;
      version = &quot;~&gt; 3&quot;
    }
  }
}

# These variables are the inputs for our module. Their context us best understood
# by looking at where they are used inside this module.
#
# docs: https://developer.hashicorp.com/terraform/language/values/variables
variable &quot;stage&quot;          { type = string }
variable &quot;location&quot;       { type = string }
variable &quot;cognitive_sku&quot;  { type = string }
variable &quot;vnet_cidr&quot;      { type = string }
variable &quot;subnet0_cidr&quot;   { type = string }
variable &quot;subnet1_cidr&quot;   { type = string }
variable &quot;subnet2_cidr&quot;   { type = string }
variable &quot;subnet3_cidr&quot;   { type = string }

###########
# PREFACE #
###########

# Past this point, documentation is mostly non-existent on my part.
# This is primarily due to the wall clock time I had available to write this post.
# All of these resources do deserve documentation to some extent!

####################
# SHARED RESOURCES #
####################

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group
resource &quot;azurerm_resource_group&quot; &quot;default&quot; {
  name     = &quot;azure-openai-${var.stage}&quot;
  location = var.location
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_network
resource &quot;azurerm_virtual_network&quot; &quot;default&quot; {
  name                = &quot;azure-openai-${var.stage}&quot;
  address_space       = [var.vnet_cidr]
  location            = azurerm_resource_group.default.location
  resource_group_name = azurerm_resource_group.default.name
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/route_table
resource &quot;azurerm_route_table&quot; &quot;default&quot; {
  name                = &quot;azure-openai-${var.stage}&quot;
  location            = azurerm_resource_group.default.location
  resource_group_name = azurerm_resource_group.default.name
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/route
resource &quot;azurerm_route&quot; &quot;local&quot; {
  name                = &quot;local&quot;
  resource_group_name = azurerm_resource_group.default.name
  route_table_name    = azurerm_route_table.default.name
  address_prefix      = var.vnet_cidr
  next_hop_type       = &quot;VnetLocal&quot;
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/route
resource &quot;azurerm_route&quot; &quot;internet&quot; {
  name                = &quot;internet&quot;
  resource_group_name = azurerm_resource_group.default.name
  route_table_name    = azurerm_route_table.default.name
  address_prefix      = &quot;0.0.0.0/0&quot;
  next_hop_type       = &quot;Internet&quot;
}

####################
# PUBLIC RESOURCES #
####################

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet
resource &quot;azurerm_subnet&quot; &quot;subnet2&quot; {
  name                 = &quot;subnet2&quot;
  resource_group_name  = azurerm_resource_group.default.name
  virtual_network_name = azurerm_virtual_network.default.name
  address_prefixes     = [var.subnet2_cidr]
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet
resource &quot;azurerm_subnet&quot; &quot;subnet3&quot; {
  name                 = &quot;subnet3&quot;
  resource_group_name  = azurerm_resource_group.default.name
  virtual_network_name = azurerm_virtual_network.default.name
  address_prefixes     = [var.subnet3_cidr]
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet_route_table_association
resource &quot;azurerm_subnet_route_table_association&quot; &quot;subnet2&quot; {
  subnet_id      = azurerm_subnet.subnet2.id
  route_table_id = azurerm_route_table.default.id
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet_route_table_association
resource &quot;azurerm_subnet_route_table_association&quot; &quot;subnet3&quot; {
  subnet_id      = azurerm_subnet.subnet3.id
  route_table_id = azurerm_route_table.default.id
}

#####################
# PRIVATE RESOURCES #
#####################

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet
resource &quot;azurerm_subnet&quot; &quot;subnet0&quot; {
  name                                          = &quot;subnet0&quot;
  resource_group_name                           = azurerm_resource_group.default.name
  virtual_network_name                          = azurerm_virtual_network.default.name
  address_prefixes                              = [var.subnet0_cidr]
  service_endpoints                             = [&quot;Microsoft.CognitiveServices&quot;]
  private_link_service_network_policies_enabled = true
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet
resource &quot;azurerm_subnet&quot; &quot;subnet1&quot; {
  name                                          = &quot;subnet1&quot;
  resource_group_name                           = azurerm_resource_group.default.name
  virtual_network_name                          = azurerm_virtual_network.default.name
  address_prefixes                              = [var.subnet1_cidr]
  service_endpoints                             = [&quot;Microsoft.CognitiveServices&quot;]
  private_link_service_network_policies_enabled = true
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet_route_table_association
resource &quot;azurerm_subnet_route_table_association&quot; &quot;subnet0&quot; {
  subnet_id      = azurerm_subnet.subnet0.id
  route_table_id = azurerm_route_table.default.id
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet_route_table_association
resource &quot;azurerm_subnet_route_table_association&quot; &quot;subnet1&quot; {
  subnet_id      = azurerm_subnet.subnet1.id
  route_table_id = azurerm_route_table.default.id
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/public_ip
resource &quot;azurerm_public_ip&quot; &quot;nat0&quot; {
  name                = &quot;azure-openai-${var.stage}-nat0&quot;
  location            = azurerm_resource_group.default.location
  resource_group_name = azurerm_resource_group.default.name
  allocation_method   = &quot;Static&quot;
  sku                 = &quot;Standard&quot;
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/nat_gateway
resource &quot;azurerm_nat_gateway&quot; &quot;nat0&quot; {
  name                = &quot;azure-openai-${var.stage}-nat0&quot;
  location            = azurerm_resource_group.default.location
  resource_group_name = azurerm_resource_group.default.name
  sku_name            = &quot;Standard&quot;
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/nat_gateway_public_ip_association
resource &quot;azurerm_nat_gateway_public_ip_association&quot; &quot;nat0&quot; {
  nat_gateway_id       = azurerm_nat_gateway.nat0.id
  public_ip_address_id = azurerm_public_ip.nat0.id
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet_nat_gateway_association
resource &quot;azurerm_subnet_nat_gateway_association&quot; &quot;nat0&quot; {
  subnet_id      = azurerm_subnet.subnet0.id
  nat_gateway_id = azurerm_nat_gateway.nat0.id
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/public_ip
resource &quot;azurerm_public_ip&quot; &quot;nat1&quot; {
  name                = &quot;azure-openai-${var.stage}-nat1&quot;
  location            = azurerm_resource_group.default.location
  resource_group_name = azurerm_resource_group.default.name
  allocation_method   = &quot;Static&quot;
  sku                 = &quot;Standard&quot;
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/nat_gateway
resource &quot;azurerm_nat_gateway&quot; &quot;nat1&quot; {
  name                = &quot;azure-openai-${var.stage}-nat1&quot;
  location            = azurerm_resource_group.default.location
  resource_group_name = azurerm_resource_group.default.name
  sku_name            = &quot;Standard&quot;
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/nat_gateway_public_ip_association
resource &quot;azurerm_nat_gateway_public_ip_association&quot; &quot;nat1&quot; {
  nat_gateway_id       = azurerm_nat_gateway.nat1.id
  public_ip_address_id = azurerm_public_ip.nat1.id
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet_nat_gateway_association
resource &quot;azurerm_subnet_nat_gateway_association&quot; &quot;nat1&quot; {
  subnet_id      = azurerm_subnet.subnet1.id
  nat_gateway_id = azurerm_nat_gateway.nat1.id
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_dns_zone
resource &quot;azurerm_private_dns_zone&quot; &quot;openai&quot; {
  name                = &quot;azure-openai-${var.stage}.privatelink.openai.azure.com&quot;
  resource_group_name = azurerm_resource_group.default.name
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_dns_zone_virtual_network_link
resource &quot;azurerm_private_dns_zone_virtual_network_link&quot; &quot;openai&quot; {
  name                  = &quot;azure-openai-${var.stage}&quot;
  resource_group_name   = azurerm_resource_group.default.name
  private_dns_zone_name = azurerm_private_dns_zone.openai.name
  virtual_network_id    = azurerm_virtual_network.default.id
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint
resource &quot;azurerm_private_endpoint&quot; &quot;private0&quot; {
  name                = &quot;azure-openai-${var.stage}-private0&quot;
  location            = azurerm_resource_group.default.location
  resource_group_name = azurerm_resource_group.default.name
  subnet_id           = azurerm_subnet.subnet0.id

  private_service_connection {
    name                           = &quot;azure-openai-${var.stage}-private0&quot;
    private_connection_resource_id = azurerm_cognitive_account.private.id
    subresource_names              = [&quot;account&quot;]
    is_manual_connection           = false
  }

  private_dns_zone_group {
    name                 = &quot;default&quot;
    private_dns_zone_ids = [azurerm_private_dns_zone.openai.id]
  }
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint
resource &quot;azurerm_private_endpoint&quot; &quot;private1&quot; {
  name                = &quot;azure-openai-${var.stage}-private1&quot;
  location            = azurerm_resource_group.default.location
  resource_group_name = azurerm_resource_group.default.name
  subnet_id           = azurerm_subnet.subnet1.id

  private_service_connection {
    name                           = &quot;azure-openai-${var.stage}-private1&quot;
    private_connection_resource_id = azurerm_cognitive_account.private.id
    subresource_names              = [&quot;account&quot;]
    is_manual_connection           = false
  }

  private_dns_zone_group {
    name                 = &quot;default&quot;
    private_dns_zone_ids = [azurerm_private_dns_zone.openai.id]
  }
}

#######################
# COGNITIVE RESOURCES #
#######################

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cognitive_account
resource &quot;azurerm_cognitive_account&quot; &quot;private&quot; {
  name                               = &quot;azure-openai-${var.stage}&quot;
  location                           = azurerm_resource_group.default.location
  resource_group_name                = azurerm_resource_group.default.name
  kind                               = &quot;OpenAI&quot;
  sku_name                           = var.cognitive_sku
  outbound_network_access_restricted = false
  local_auth_enabled                 = true

  public_network_access_enabled = true
  # public_network_access_enabled is a misleading setting name. It is best understood in
  # connection with the network_acls.defeault_action setting. Here&apos;s effect of various
  # combinations of these two settings:
  #
  # public_network_access_enabled: true, default_action: Allow
  #   - Accessible anyhere from the internet. THIS IS DANGEROUS.
  #
  # public_network_access_enabled: true, default_action: Deny
  #   - Accessible from the specified IP ranges in `ip_rules`.
  #
  # public_network_access_enabled: false, default_action: Allow
  #   - Adding `ip_rules` has no effect, they&apos;re fake news.
  #
  # public_network_access_enabled: false, default_action: Deny
  #   - Same as above.
  #
  # At a high level, this setting needs only to be `true` when you haven&apos;t yet
  # setup private network ingress points from your primary network group (vnet, VPC, VPN, etc)
  # to this new vnet. Explaining that in detail is beyond the scope of this post!

  custom_subdomain_name = &quot;azure-openai-${var.stage}-v1&quot;
  # custom_subdomain_name has a version incrementor because it&apos;s a global resource,
  # and doesn&apos;t always get deleted immediately when the resource is destroyed.
  # The error message you get will look something like:
  #
  # &gt; The subdomain name ... is not available as it&apos;s already used by a resource
  #
  # When I get that message, I simply increment the version.

  network_acls {
    default_action = &quot;Deny&quot;
    ##########################################
    # !!! IMPORTANT SECURITY ACTION ITEM !!! #
    ##########################################
    # This is a &quot;prototype&quot; configuration where you just grab your IP address
    # via `$ curl http://ifconfig.me` and stick it right this file. This is bad
    # security practice and is only fit for proving to your stackholders that
    # you are skilled enough to deploy Azure OpenAI via terraform.
    #
    # What you want to do, is setup a peering connection from your main network groups
    # to the new network groups created by this terraform file. You will need
    # to setup the peering connection to support network requests from humans
    # (via a VPN or similar) and network requests from automated services
    # (via another network group, like a different vnet).
    #
    # Once you do that, you can set `public_network_access_enabled = false`,
    # because the network requests will be coming from private IPs routed through
    # your peering connection. Then you will set these ip_rules to private cidrs.
    ip_rules = [
      &quot;255.255.255.255&quot;, # &amp;#x3C;= !!! your IP address goes here !!!
    ]
    virtual_network_rules {
      subnet_id = azurerm_subnet.subnet0.id
    }
    virtual_network_rules {
      subnet_id = azurerm_subnet.subnet1.id
    }
  }
}

# docs: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cognitive_deployment
resource &quot;azurerm_cognitive_deployment&quot; &quot;private&quot; {
  name                 = &quot;azure-openai-${var.stage}&quot;
  cognitive_account_id = azurerm_cognitive_account.private.id
  model {
    format  = &quot;OpenAI&quot;
    name    = &quot;gpt-35-turbo&quot;
    version = &quot;0301&quot;
  }

  scale {
    type = &quot;Standard&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All of that! Should be deploy-able with a &lt;code&gt;terraform init &amp;#x26;&amp;#x26; terraform apply&lt;/code&gt; without requiring much additional configuration. With, of course, the significant except of putting your IP address into the &lt;code&gt;ip_rules&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I don&apos;t expect this example will quite work so easily if you are working with existing architecture. If you&apos;re looking for another example terraform configuration to compare against your architecture, I would recommend this Github repo from Azure:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/Azure-Samples/azure-openai-terraform-deployment-sample&quot;&gt;https://github.com/Azure-Samples/azure-openai-terraform-deployment-sample&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Good luck! And do follow-up with those network security improvements, dear reader.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[On Permissions Models for Cloud Platform Providers]]></title><description><![CDATA[If I were doing permissions for a cloud platform provider, I'd do them like this!]]></description><link>https://coilysiren.me/posts/on-permissions-models-for-cloud-platform-providers/</link><guid isPermaLink="false">https://coilysiren.me/posts/on-permissions-models-for-cloud-platform-providers/</guid><dc:creator><![CDATA[@coilysiren]]></dc:creator><pubDate>Sun, 12 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I was &lt;a href=&quot;https://twitter.com/coilysiren/status/1215860950684667904&quot;&gt;recently reflecting&lt;/a&gt; on the fact that &lt;a href=&quot;https://aws.amazon.com/iam/&quot;&gt;AWS IAM&lt;/a&gt; has the best permissions model on the market, and wondering why I think most other platforms have such a dramatically inferior setup. I had never put much thought into this prior, so my first conclusion was &lt;em&gt;&quot;wow everyone but Amazon is bad at this!&quot;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Except that&apos;s not actually how the world works 🙂 In reality there are concrete tradeoffs and decisions that lead to products being in a certain state. And in situations like this? A well designed foundation will get you 90% of the way there. With that context I imagined: how would I design a platform such that future people working on it &lt;em&gt;(eg. without my direct assistance)&lt;/em&gt; would replicate the success and effectiveness of IAM? This post is me taking a shot at that.&lt;/p&gt;
&lt;h2&gt;Guiding Principles&lt;/h2&gt;
&lt;p&gt;To start, there&apos;s a few principles we want to establish. The principles establish the direction and essence of our foundation, and all of the large design choices will flow from the principles. Some of the principles are lifted verbatim from AWS, which is to be expected since AWS is the clear leader in it&apos;s realm. Here they are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=18916406&quot;&gt;all services must interact exclusively through public APIs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;all service endpoints and clients must be created and validated with code generation&lt;/li&gt;
&lt;li&gt;all service operations must take fully qualified unique resource identifiers (RSIDs) as inputs&lt;/li&gt;
&lt;li&gt;all service operations must only use the resources passed in as RSIDs during their operation&lt;/li&gt;
&lt;li&gt;every unique independent resource within a service must be able to by specified via a RSID&lt;/li&gt;
&lt;li&gt;if a resource is a child of another resource, that must be encoded into the RSID&lt;/li&gt;
&lt;li&gt;every resource is a child of a &quot;organization&quot;, which generally presents a company&lt;/li&gt;
&lt;li&gt;every unique independent action that can be taken on a service must have a unique action identifier (ACID)&lt;/li&gt;
&lt;li&gt;ACIDs must follow the Create Read Update Delete (CRUD) naming scheme, but may also include additional verbs such as &quot;List&quot;&lt;/li&gt;
&lt;li&gt;IAM is the only service that gets any degree of special treatment&lt;/li&gt;
&lt;li&gt;the code generation must wrap every service operation with the same IAM checks&lt;/li&gt;
&lt;li&gt;the platform must be capable of running itself&lt;/li&gt;
&lt;li&gt;service usage can be simplified for customers by creating aliases that group operations, but never by compromising on the functionality of the operations themselves&lt;/li&gt;
&lt;li&gt;everything defaults to allowing no access&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;An example case&lt;/h2&gt;
&lt;p&gt;With the principles above, I want to detail an example case. Say our cloud platform (&lt;code&gt;cloudOps&lt;/code&gt;) just launched, and we want to provide a database service (&lt;code&gt;kittenDB&lt;/code&gt;) to our clients. Our platform itself also runs on that same database service! How do we go about setting that up? We start by defining all the RSIDs and ACIDs for both our permissions service (&lt;code&gt;IAM&lt;/code&gt;) and our database service (&lt;code&gt;kittenDB&lt;/code&gt;), both for our use and for use by our clients.&lt;/p&gt;
&lt;p&gt;Service IAM has 3 resources: &quot;users&quot;, &quot;policies&quot;, and &quot;organizations&quot;. Policies can be granted to users and organizations. The complete layout of ids is like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;RSID::IAM::{{ organization }}::/organization/*
RSID::IAM::{{ organization }}::/user/*
RSID::IAM::{{ organization }}::/policy/*
RSID::IAM::{{ organization }}::/policy/{{ policy }}/user/*
RSID::IAM::{{ organization }}::/policy/{{ policy }}/organization/*

ACID::IAM::create-organization
ACID::IAM::list-organization
ACID::IAM::read-organization
ACID::IAM::update-organization
ACID::IAM::delete-organization

ACID::IAM::create-user
ACID::IAM::list-user
ACID::IAM::read-user
ACID::IAM::update-user
ACID::IAM::delete-user

ACID::IAM::create-policy
ACID::IAM::list-policy
ACID::IAM::read-policy
ACID::IAM::update-policy
ACID::IAM::delete-policy

ACID::IAM::attach-policy-to-user
ACID::IAM::attach-policy-to-organization
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Service kittenDB has 2 resources: &quot;maps&quot; and &quot;entries&quot;, in addition to the database itself. Entries are contained with maps. The complete layout of ids is like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;RSID::KITTENDB::{{ organization }}::/database/*
RSID::KITTENDB::{{ organization }}::/map/*
RSID::KITTENDB::{{ organization }}::/map/{{ map }}/entry/*

ACID::KITTENDB::create-database
ACID::KITTENDB::read-database
ACID::KITTENDB::update-database
ACID::KITTENDB::delete-database

ACID::KITTENDB::create-map
ACID::KITTENDB::read-map
ACID::KITTENDB::update-map
ACID::KITTENDB::delete-map

ACID::KITTENDB::create-entry-in-map
ACID::KITTENDB::read-entry-in-map
ACID::KITTENDB::update-entry-in-map
ACID::KITTENDB::delete-entry-in-map
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Platform Bootstrapping&lt;/h2&gt;
&lt;p&gt;The very very first thing we need to do, is allow the platform to manage itself. And in the beginning... there is no platform! So someone at cloudOps need to run some commands to spin up the very first kittenDB instance. We&apos;ll assume this some employee on the physical cloudDB server running &lt;code&gt;make build&lt;/code&gt; after doing &lt;code&gt;git clone kittenDB ...&lt;/code&gt; or whatever.&lt;/p&gt;
&lt;p&gt;From there we have to &quot;hand load&quot; a few policies into the database. Our goal here is to do all of the manual direct-to-database work required in order to let our users utilize the platform. It&apos;s important to recall here that cloudOps is using kittenDB as it&apos;s database, and every action requires a database call, so for even very mundane actions like &quot;organization creation&quot; we need to start defining access policies. We&apos;ll do this with a single instance &quot;global policy&quot; that applies to everyone. It&apos;ll look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;# in case you&apos;re unacquainted, this is cloudformation syntax!

GlobalPolicy:
    Name: &quot;global-policy&quot;
    Type: &quot;IAM::Policy&quot;
    Properties:
        Rules:
            - Name: &quot;allow-creating-organizations&quot;
              Resources:
                # for any organization name
                - &quot;IAM::*::/organization/*&quot;
              Actions:
                # allow seeing if an organization of that name exists
                - &quot;IAM::list-organization&quot;
                # allow creating an organization with that name
                - &quot;IAM::create-organization&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This would translate to API calls that, in a CLI for example, would look like this&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cloud-ops iam create-organization --name &quot;my-new-organization&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;More Policy Definitions&lt;/h2&gt;
&lt;p&gt;The last section described the most basic action for the platform: organization creation. From there we have a few more &quot;personas&quot; we need to consider. The personas represent either a distinct state for a given user, or a &quot;machine user&quot; created for a specific purpose. At any rate, they all need policies!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;# this is for an &quot;admin&quot; user for the organization
OrgAdmin:
    Name: &quot;org-admin&quot;
    Type: &quot;IAM::Policy&quot;
    Properties:
        Rules:
            - Name: &quot;allow-managing-my-organization&quot;
              Resources:
                # for anything within my organization
                - &quot;IAM::my-new-organization::*&quot;
              Actions:
                # allow me to do anything
                - &quot;IAM::*&quot;
            - Name: &quot;allow-database-access&quot;
              Resources:
                # for a database within my organization
                - &quot;KITTENDB::my-new-organization::*&quot;
              Actions:
                # allow &quot;database&quot; level actions such as creating and deleting
                # the database, but not reading its contents
                - &quot;KITTENDB::*database*&quot;

# this is for an &quot;operator&quot; user for the organization, such as an engineer
OrgOperator:
    Name: &quot;org-operator&quot;
    Type: &quot;IAM::Policy&quot;
    Properties:
        Rules:
            - Name: &quot;allow-database-access&quot;
              Resources:
                # for a database within my organization
                - &quot;KITTENDB::my-new-organization::*&quot;
              Actions:
                # allow me to see anything I might need to for debugging
                - &quot;KITTENDB::*list*&quot;
                - &quot;KITTENDB::*read*&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;# these are for two distinct types of &quot;machine user&quot;

MachineUserReadAccess:
    Name: &quot;machine-read-access&quot;
    Type: &quot;IAM::Policy&quot;
    Properties:
        Rules:
            - Name: &quot;database-read&quot;
              Resources:
                # for a database within my organization
                - &quot;KITTENDB::my-new-organization::*&quot;
              Actions:
                # allow read actions
                - &quot;KITTENDB::*read*&quot;
                - &quot;KITTENDB::*list*&quot;

MachineUserWriteAccess:
    Name: &quot;machine-write-access&quot;
    Type: &quot;IAM::Policy&quot;
    Properties:
        Rules:
            - Name: &quot;database-read&quot;
              Resources:
                # for a database within my organization
                - &quot;KITTENDB::my-new-organization::*&quot;
              Actions:
                # allow read actions
                - &quot;KITTENDB::*read*&quot;
                - &quot;KITTENDB::*list*&quot;
            - Name: &quot;database-contents-write&quot;
              Resources:
                # for the contents of my organization&apos;s database
                - &quot;KITTENDB::my-new-organization::/map/*&quot;
              Actions:
                # allow all actions
                - &quot;KITTENDB::*&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These policies would be pre-populated in the user&apos;s new organization, so they could assign them to people as needed. So you would create your new organization, and be presented with a list of policies like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;org-admin&lt;/li&gt;
&lt;li&gt;org-operator&lt;/li&gt;
&lt;li&gt;machine-read-access&lt;/li&gt;
&lt;li&gt;machine-write-access&lt;/li&gt;
&lt;li&gt;etc etc&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The policies would be editable and for whatever specific purpose the user requires. A fairly common one would likely be giving read / write access only to certain paths:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;Rules:
    - Name: &quot;read-shared-paths&quot;
      Resources:
        # for all shared paths
        - &quot;KITTENDB::my-new-organization::/map/shared/*&quot;
      Actions:
        # allow read actions
        - &quot;KITTENDB::*read*&quot;
        - &quot;KITTENDB::*list*&quot;
    - Name: &quot;write-my-shared-paths&quot;
      Resources:
        # for my shared paths
        - &quot;KITTENDB::my-new-organization::/map/shared/my-paths/*&quot;
      Actions:
        # allow all actions
        - &quot;KITTENDB::*&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Implementation Challenges&lt;/h2&gt;
&lt;p&gt;This is an &quot;overview&quot; level description of how one would setup a system like this, but there are a great many things that represent implementation challenges. Including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;given the auto-generated stub kittenDB API endpoints, how do you implement the actual logic?&lt;/li&gt;
&lt;li&gt;given how &quot;hot&quot; the code and data paths will be for the IAM access systems, how do you keep performance high?&lt;/li&gt;
&lt;li&gt;how do you resolve intersecting / conflicting access policies, ideally in such a way that users can debug them?&lt;/li&gt;
&lt;li&gt;how do you consistently ensure that every single API endpoint effectively communicates to the user when there&apos;s a permissions issue?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those are all &quot;tactical&quot; level challenges that will remain relevant for the entire course of the business. You&apos;ll need people dedicated to solving them, and also people working on expanding cloudOps out horizontally to support more types of data stores. That said! All of that ongoing work would happen on top of the existing strong fundamental permissions model, and you would be well suited to provide high flexibility access control for any client&apos;s business needs. You could also look into expanding the permissions model for more fine grain cases 👀 such as &lt;a href=&quot;https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html&quot;&gt;attribute and tag based conditions&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Golang PR Field Notes (Part 3)]]></title><description><![CDATA[In this part, we start building foundational knowledge.]]></description><link>https://coilysiren.me/posts/golang-pr-notes-3/</link><guid isPermaLink="false">https://coilysiren.me/posts/golang-pr-notes-3/</guid><dc:creator><![CDATA[@coilysiren]]></dc:creator><pubDate>Sun, 29 Dec 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;if you&apos;re looking for part 2, &lt;a href=&quot;https://coilysiren.me/posts/2019-12-26-golang-pr-field-notes-part-2/&quot;&gt;it&apos;s back this way &amp;#x3C;===&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;When we last saw our protagonist&lt;/h2&gt;
&lt;p&gt;The last time we were here, we took the time to identify some concepts we may need to understand, in order to do our work. That list of concepts is very long, and the actual relevance of the individual concepts varies fairly dramatically.&lt;/p&gt;
&lt;p&gt;Today, we&apos;re going to build up the knowledge we don&apos;t have, and start identifying and defining our &quot;foundational concepts&quot;. Those are the things we &lt;em&gt;absolutely need to understand&lt;/em&gt; in order to do any well grounded work in this space.&lt;/p&gt;
&lt;h2&gt;The foundational concepts&lt;/h2&gt;
&lt;p&gt;Judging from how they were mentioned and the frequency of mentions, here&apos;s what I think I need to understand first. I&apos;ve included links to the place where they appear in the golang issue tracker, links to various places on the internet that contain definitions or helpful information, and finally my personal re-definition of the term.&lt;/p&gt;
&lt;h3&gt;high level programming concepts&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;code compilation&lt;/strong&gt; &lt;em&gt;(&lt;a href=&quot;https://en.wikipedia.org/wiki/Compiler&quot;&gt;wikipedia&lt;/a&gt;)&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;In a general sense, &lt;strong&gt;code compilation&lt;/strong&gt; is any process of turning one programming language into another. It&apos;s most common specific usage is in turning a high level language (like python or go) into a low level language (like assembly or machine code) to create an executable.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;compiler optimization&lt;/strong&gt; &lt;em&gt;(&lt;a href=&quot;https://en.wikipedia.org/wiki/Optimizing_compiler&quot;&gt;wikipedia&lt;/a&gt;)&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;compiler optimization&lt;/strong&gt; is the practice of trying to optimize the process of code compilation for any of the following attributes: compile time, code output size, memory requirements, and power consumption.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;assembly code&lt;/strong&gt; &lt;em&gt;(&lt;a href=&quot;https://en.wikipedia.org/wiki/Code_generation_(compiler)&quot;&gt;wikipedia&lt;/a&gt;)&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;assembly code&lt;/strong&gt; is the code that computers actually know how to read. Most compilers work by turning some source code into some machine code, where the target machine code is often an assembly language. It&apos;s possible, but very annoying, to write assembly code directly.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;computer processor concepts&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;inter process communication (IPC)&lt;/strong&gt; &lt;em&gt;(&lt;a href=&quot;https://github.com/golang/go/issues/15734#issuecomment-220172384&quot;&gt;github&lt;/a&gt;) (&lt;a href=&quot;https://en.wikipedia.org/wiki/Inter-process_communication&quot;&gt;wikipedia&lt;/a&gt;)&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;by &quot;default&quot; processes share no memory and cannot directly communicate with each other. &lt;strong&gt;IPC&lt;/strong&gt; is a set of techniques on can use to share data between processes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;semaphore&lt;/strong&gt; &lt;em&gt;(&lt;a href=&quot;https://github.com/golang/go/issues/15734#issuecomment-220172384&quot;&gt;github&lt;/a&gt;) (&lt;a href=&quot;https://en.wikipedia.org/wiki/Semaphore_(programming)&quot;&gt;wikipedia&lt;/a&gt;)&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;a &lt;strong&gt;semaphore&lt;/strong&gt; is a construct that is used to control access to shared data across multiple concurrent processes. Semaphores are conceptually related to UNIX pipes, although it&apos;s unclear to me if pipes specifically are semaphores. In general, semaphores become required for certain kinds of complex work across multiple processes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;instruction cache (icache)&lt;/strong&gt; &lt;em&gt;(&lt;a href=&quot;https://github.com/golang/go/issues/29067#issuecomment-443887583&quot;&gt;github&lt;/a&gt;) (&lt;a href=&quot;https://en.wikipedia.org/wiki/CPU_cache#ICACHE&quot;&gt;wikipedia&lt;/a&gt;)&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;a cache is a place where you store things so that you can access them more quickly. An &lt;strong&gt;icache&lt;/strong&gt; is a cache for instructions to be run by the CPU. Instructions in this context are lines of source code that are compiled to some machine code. CPUs may have some mechanism for moving instructions into / out of the icache, but that&apos;s a subject for another day.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;registers&lt;/strong&gt; &lt;em&gt;(&lt;a href=&quot;https://github.com/golang/go/issues/25999#issuecomment-399196221&quot;&gt;github&lt;/a&gt;) (&lt;a href=&quot;https://en.wikipedia.org/wiki/Processor_register&quot;&gt;wikipedia&lt;/a&gt;)&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;processor &lt;strong&gt;registers&lt;/strong&gt; are a type of cache used by the CPU. They are the top of the pyramid in the &lt;a href=&quot;https://en.wikipedia.org/wiki/Memory_hierarchy&quot;&gt;memory hierarchy&lt;/a&gt;, which is to say that they are the fastest memory location available to the CPU. Registers are very very small, their maximum size typically being registered in bits. The amount of memory available to registers (~32 bits) can be contrasted with the memory available to RAM (~8GB, so 10^9 larger) and the memory available to hard drives (~1TB, so 10^12 larger)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;general compilation concepts&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;bounds checks&lt;/strong&gt; &lt;em&gt;(github &lt;a href=&quot;https://github.com/golang/go/issues/17566&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;https://github.com/golang/go/issues/25862&quot;&gt;2&lt;/a&gt;) (&lt;a href=&quot;https://en.wikipedia.org/wiki/Bounds_checking&quot;&gt;wikipedia&lt;/a&gt;)&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;bounds checking&lt;/strong&gt; is a process via which a CPU can check that a variable meets some conditions before it is used. One common type of bounds checking is range checking, where a variable is checked to see if it fits into a given type, like a u8 (unsigned 8 bit int) or i16 (signed 16 bit int). Another common type of bounds checking is index checking, where an index to an array is checked to see if it actually fits in that array.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;compiled function nodes / abstract syntax trees (ASTs)&lt;/strong&gt; &lt;em&gt;(&lt;a href=&quot;https://github.com/golang/go/issues/17566&quot;&gt;github&lt;/a&gt;) (&lt;a href=&quot;https://en.wikipedia.org/wiki/Abstract_syntax_tree&quot;&gt;wikipedia&lt;/a&gt;)&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ASTs&lt;/strong&gt; are an abstract representation of some source code, generally created by some kind of parser or compiler. ASTs represent certain elements as nodes in the tree, such as functions, if statements, and comparisons. The term &lt;strong&gt;compiled function node&lt;/strong&gt; can refer to either the literal individual node in an AST representing the function, or the individual function node and all of it&apos;s children in the tree.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;inlining functions&lt;/strong&gt; &lt;em&gt;(&lt;a href=&quot;https://github.com/golang/go/issues/17566&quot;&gt;github&lt;/a&gt;) (&lt;a href=&quot;https://en.wikipedia.org/wiki/Inline_expansion&quot;&gt;wikipedia&lt;/a&gt;)&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;this concept primarily relates to compiler optimization. For some parent function that calls some child function, the child function can either be represented as a separate function &quot;node&quot; - or it can be &lt;strong&gt;inlined&lt;/strong&gt; into the parent &lt;strong&gt;function&lt;/strong&gt; in a manner functionally equivalent to copy pasting. The dynamics of when something should vs should not be inlined are a matter of long and frequent discussion within golang, in particular &lt;a href=&quot;https://github.com/golang/go/issues/17566&quot;&gt;in this issue&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;intrinsic functions&lt;/strong&gt; &lt;em&gt;(&lt;a href=&quot;https://github.com/golang/go/issues/17566#issuecomment-417111586&quot;&gt;github&lt;/a&gt;) (&lt;a href=&quot;https://en.wikipedia.org/wiki/Intrinsic_function&quot;&gt;wikipedia&lt;/a&gt;)&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;intrinsic functions&lt;/strong&gt; are special and fancy functions within a language, that have some extra tricks that leverage more low level processor / memory resources. They&apos;re sometimes written directly in assembly, and are often architecture specific (eg. like specific to AMD processors).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;compilation concepts with large impact on golang&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;static single assignment (SSA)&lt;/strong&gt; &lt;em&gt;(github &lt;a href=&quot;https://github.com/golang/go/issues/8893#issuecomment-134458436&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;https://github.com/golang/go/issues/15736#issue-155612156&quot;&gt;2&lt;/a&gt;, &lt;a href=&quot;https://github.com/golang/go/issues/17566#issuecomment-256149722&quot;&gt;3&lt;/a&gt;) (&lt;a href=&quot;https://godoc.org/golang.org/x/tools/go/ssa&quot;&gt;godoc&lt;/a&gt;) (&lt;a href=&quot;https://en.wikipedia.org/wiki/Static_single_assignment_form&quot;&gt;wikipedia&lt;/a&gt;)&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SSA&lt;/strong&gt; is a compiler design mechanism, wherein each variable is assigned to be assigned once and only once. In SSA, if a new value is assigned to a variable then a new version of that variable if created. SSA is primarily used for compiler optimization, since the guarantee of &quot;a variable&apos;s value will never change&quot; can enable a variety of compiler optimization algorithms.&lt;/li&gt;
&lt;li&gt;golang has a large &lt;a href=&quot;https://godoc.org/golang.org/x/tools/go/ssa&quot;&gt;SSA package&lt;/a&gt; that does various activities related to SSA.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;binary files / executables&lt;/strong&gt; &lt;em&gt;(github &lt;a href=&quot;https://github.com/golang/go/issues/4719#issuecomment-66074026&quot;&gt;1&lt;/a&gt; &lt;a href=&quot;https://github.com/golang/go/issues/4719#issuecomment-470268217&quot;&gt;2&lt;/a&gt; &lt;a href=&quot;https://github.com/golang/go/issues/20070#issuecomment-296292391&quot;&gt;3&lt;/a&gt;) (&lt;a href=&quot;https://en.wikipedia.org/wiki/Executable&quot;&gt;wikipedia&lt;/a&gt;)&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;An &lt;strong&gt;executable&lt;/strong&gt; is a file that can be run by a machine in order to do some task. Executable files contrast most sharply with data files, which do not contain task instructions and instead contain information. Many executables are &lt;strong&gt;binary files&lt;/strong&gt; mean to be run by a machine directly, although the term executable can also be applied to the source code files of scripting languages (like python, and &lt;em&gt;unlike go&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;In golang care has been taken to make the generation of executable binary files both fast and reliable.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;*.o&lt;/code&gt; object files&lt;/strong&gt; &lt;em&gt;(github &lt;a href=&quot;https://github.com/golang/go/issues/4719#issuecomment-144125808&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;https://github.com/golang/go/issues/15734#issuecomment-339211682&quot;&gt;2&lt;/a&gt;) (&lt;a href=&quot;https://go.googlesource.com/proposal/+/master/design/14386-zip-package-archives.md&quot;&gt;googlesource&lt;/a&gt;) (wikipedia &lt;a href=&quot;https://en.wikipedia.org/wiki/Object_file&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Object_code&quot;&gt;2&lt;/a&gt;)&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;object files&lt;/strong&gt; are the output of a compiler, and it&apos;s contents are usually in a machine language such as binary. Object files usually contain data points that a linker can use to fill in code from other places.&lt;/li&gt;
&lt;li&gt;Go&apos;s compiler produces object files as one of the compiler outputs.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;*.a&lt;/code&gt; files&lt;/strong&gt; &lt;em&gt;(github &lt;a href=&quot;https://github.com/golang/go/issues/4719&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;https://github.com/golang/go/issues/15681#issuecomment-380967328&quot;&gt;2&lt;/a&gt;, &lt;a href=&quot;https://github.com/golang/go/issues/15734#issuecomment-220173676&quot;&gt;3&lt;/a&gt;) (&lt;a href=&quot;https://stackoverflow.com/questions/15551293/what-are-a-files-in-go&quot;&gt;stackoverflow&lt;/a&gt;) (&lt;a href=&quot;https://go.googlesource.com/proposal/+/master/design/14386-zip-package-archives.md&quot;&gt;googlesource&lt;/a&gt;) (&lt;a href=&quot;https://en.wikipedia.org/wiki/Ar_(Unix)&quot;&gt;wikipedia&lt;/a&gt;)&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;Go&apos;s &lt;code&gt;*.a&lt;/code&gt; files are package archive files, they were originally in the &lt;a href=&quot;https://en.wikipedia.org/wiki/Ar_(Unix)&quot;&gt;ar archive format&lt;/a&gt;. There was at some point &lt;a href=&quot;https://github.com/golang/go/issues/14386&quot;&gt;a proposal&lt;/a&gt; to change them to the more standard zip file format. Archive files contain compiled package code, and additionally some debugging information.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;file linking / static vs dynamic linking&lt;/strong&gt; &lt;em&gt;(github &lt;a href=&quot;https://github.com/golang/go/issues/14271&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;https://github.com/golang/go/issues/4719#issuecomment-144125808&quot;&gt;2&lt;/a&gt;, &lt;a href=&quot;https://github.com/golang/go/issues/15681#issuecomment-240319812&quot;&gt;3&lt;/a&gt;) (&lt;a href=&quot;https://en.wikipedia.org/wiki/Static_library&quot;&gt;wikipedia&lt;/a&gt;) (stackoverflow &lt;a href=&quot;https://stackoverflow.com/questions/26418883/golang-how-to-link-c-objects-using-cgo-ofiles&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;https://stackoverflow.com/questions/1993390/static-linking-vs-dynamic-linking&quot;&gt;2&lt;/a&gt;) (&lt;a href=&quot;https://www.reddit.com/r/golang/comments/53nl4b/why_all_go_binaries_are_staticallylinked/&quot;&gt;reddit&lt;/a&gt;)&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;linking&lt;/strong&gt; is the process by which an object file can request that other code be inserted into it. &lt;strong&gt;dynamic linking&lt;/strong&gt; is a linking process where the linking is not truly resolved until runtime, creating a dependency on external files (like DDLs). &lt;strong&gt;static linking&lt;/strong&gt; is a linking process where any links are resolved at compile time, creating a fully self-contained object.&lt;/li&gt;
&lt;li&gt;static linking is the better choice in like 99% of all cases.&lt;/li&gt;
&lt;li&gt;golang&apos;s compiler uses static linking, either exclusively or by default (I&apos;m not sure which).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;export data&lt;/strong&gt; &lt;em&gt;(github &lt;a href=&quot;https://github.com/golang/go/issues/15752&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;https://github.com/golang/go/issues/15734&quot;&gt;2&lt;/a&gt;, &lt;a href=&quot;https://github.com/golang/go/issues/20070&quot;&gt;3&lt;/a&gt;) (godoc &lt;a href=&quot;https://godoc.org/golang.org/x/tools/go/internal/gcimporter&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;https://godoc.org/golang.org/x/tools/go/gcexportdata&quot;&gt;2&lt;/a&gt;, &lt;a href=&quot;https://godoc.org/golang.org/x/tools/cmd/godex&quot;&gt;3&lt;/a&gt;)&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;the literal reference for &lt;em&gt;&quot;the &lt;strong&gt;data&lt;/strong&gt; that is &lt;strong&gt;exported&lt;/strong&gt; from a package&quot;&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;it&apos;s usage within golang is golang specific, because there are standard library tools (linked above) in golang built around managing export data. Those tools are responsible for the format and content of the export data.&lt;/li&gt;
&lt;li&gt;that said, the term itself is fairly generic since &quot;exported data&quot; can mean an entire universe of things.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;general computer science concepts&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;runes&lt;/strong&gt; &lt;em&gt;(&lt;a href=&quot;https://github.com/golang/go/issues/27148#issuecomment-415131187&quot;&gt;github&lt;/a&gt;) (&lt;a href=&quot;https://stackoverflow.com/questions/19310700/what-is-a-rune&quot;&gt;stackoverflow&lt;/a&gt;) (&lt;a href=&quot;https://blog.golang.org/strings&quot;&gt;blog.golang.org&lt;/a&gt;)&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;runes&lt;/strong&gt; are integer values that point to particular unicode code points.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;the stack vs the heap&lt;/strong&gt; &lt;em&gt;(&lt;a href=&quot;https://github.com/golang/go/issues/25999#issuecomment-400841088&quot;&gt;github&lt;/a&gt;) (&lt;a href=&quot;https://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap&quot;&gt;stackoverflow&lt;/a&gt;)&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;the &lt;strong&gt;stack&lt;/strong&gt; is an area of memory that uses last-in-first-out access patterns, and is used for rapid access to small data. The term stack can be remembered by thinking of someone quickly stacking a pile of plates as they&apos;re being washed.&lt;/li&gt;
&lt;li&gt;the &lt;strong&gt;heap&lt;/strong&gt; is an area of memory where access is done in an ad-hoc manner, and is used for longer term access to large data. In general, high level scripting languages (ex. ruby and python) allocate most / all of their memory of the heap, for the sake of simplicity at the cost of performance. The term heap can be remembered by thinking of a large pile (eg. a &quot;heap&quot;) of objects scattered about randomly.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;golang general concepts&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;goroutines&lt;/strong&gt; &lt;em&gt;(github &lt;a href=&quot;https://github.com/golang/go/issues/27345&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;https://github.com/golang/go/issues/25999#issuecomment-400843932&quot;&gt;2&lt;/a&gt;) (&lt;a href=&quot;https://gobyexample.com/goroutines&quot;&gt;gobyexample&lt;/a&gt;) (&lt;a href=&quot;https://www.golang-book.com/books/intro/10&quot;&gt;golang-book&lt;/a&gt;)&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;goroutines&lt;/strong&gt; are &lt;a href=&quot;https://codeburst.io/why-goroutines-are-not-lightweight-threads-7c460c1f155f&quot;&gt;not threads&lt;/a&gt; 😅. They&apos;re go&apos;s concurrency model. goroutines are to go as processes are to a CPU.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;buffering and channels&lt;/strong&gt; &lt;em&gt;(&lt;a href=&quot;https://github.com/golang/go/issues/27345#issuecomment-419254790&quot;&gt;github&lt;/a&gt;) (&lt;a href=&quot;https://gobyexample.com/channel-buffering&quot;&gt;gobyexample&lt;/a&gt;) (&lt;a href=&quot;https://stackoverflow.com/questions/22747711/what-is-the-use-of-buffered-channels-in-go&quot;&gt;stackoverflow&lt;/a&gt;) (&lt;a href=&quot;https://medium.com/capital-one-tech/buffered-channels-in-go-what-are-they-good-for-43703871828&quot;&gt;medium&lt;/a&gt;) (&lt;a href=&quot;https://tour.golang.org/concurrency/3&quot;&gt;tour.golang.org&lt;/a&gt;)&lt;/em&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;channels&lt;/strong&gt; are like IPC except for goroutines. They allow communication and synchronization between goroutines. &lt;strong&gt;buffering&lt;/strong&gt; allows channels to act asynchronously by creating little pools (buffers) that each side can interact with without blocking.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wow! What have we learned?&lt;/h2&gt;
&lt;p&gt;...everything 😆. Interestingly, a lot (most?) of these concepts were concepts that I already had some awareness of, I just wasn&apos;t able to define them. Now empowered with these definitions, when I read issues like &lt;a href=&quot;https://github.com/golang/go/issues/15752&quot;&gt;https://github.com/golang/go/issues/15752&lt;/a&gt; I have a much much stronger understanding of what&apos;s being described.&lt;/p&gt;
&lt;h2&gt;Up next&lt;/h2&gt;
&lt;p&gt;Now that I know &lt;strong&gt;a ton&lt;/strong&gt; more about this space, I can more accurately judge whether or not a particular github issue is a good fit for me to work on. There are several types of issues that I now know aren&apos;t quite a good fit for a first-timer, for example this issue about &lt;a href=&quot;https://github.com/golang/go/issues/17566&quot;&gt;improving inlining&lt;/a&gt;. In my next post I&apos;ll setup a more targeted framework for determining who approachable the various issues are, and (🤞🏽ideally🤞🏽) pick one to start working on.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Golang PR Field Notes (Part 2)]]></title><description><![CDATA[Today we do concept exploration! Tomorrow is concept definition.]]></description><link>https://coilysiren.me/posts/golang-pr-notes-2/</link><guid isPermaLink="false">https://coilysiren.me/posts/golang-pr-notes-2/</guid><dc:creator><![CDATA[@coilysiren]]></dc:creator><pubDate>Thu, 26 Dec 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;if you&apos;re looking for part 1, &lt;a href=&quot;https://coilysiren.me/posts/2019-12-25-golang-pr-field-notes-part-1/&quot;&gt;it&apos;s back this way &amp;#x3C;===&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Starting up again&lt;/h2&gt;
&lt;p&gt;Something I under-estimated, is how amazingly useful having my notes &lt;a href=&quot;https://coilysiren.me/posts/2019-12-25-golang-pr-field-notes-part-1/&quot;&gt;from yesterday&lt;/a&gt; would be. Having written so much down gives me a much stronger launching pad for furthering my knowledge, particularly when the alternative is having to remember everything.&lt;/p&gt;
&lt;p&gt;To kick off the day, here&apos;s the list of questions I need to answer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&lt;strong&gt;what does it mean to build a dependent package, and when do we know that a dependent package needs to be rebuilt?&lt;/strong&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;strong&gt;how many more types of code change do we need to make change the export data?&lt;/strong&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;strong&gt;what are object files?&lt;/strong&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;strong&gt;how do we know when object files are stale?&lt;/strong&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;strong&gt;how do we change the export data when object files are stale?&lt;/strong&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;strong&gt;how do executables relate to needing to rebuild dependent packages?&lt;/strong&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;strong&gt;what does it mean to re-link executables?&lt;/strong&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I need to be able to answer all those questions before I can confidently make any movement here, but I actually don&apos;t want to start on that yet. What I want to do first is do more requirements gathering, because the issue we&apos;re looking at has links to other issues that may contain relevant information.&lt;/p&gt;
&lt;h2&gt;Looking through related issues&lt;/h2&gt;
&lt;p&gt;Related issues often contain context that&apos;s required to fully understand the origin issue. When working in very large repos when potentially complex histories, I read all the issues out two steps from the origin issue. So if I&apos;ve started on issue #101 which mentions #102 which mentions #103 which mentions #104, I&apos;ll read all of #101, #102, and #103 (eg. and not #104). This is essentially a triage mechanism, in a repo as large as golang I could probably spend an entire calendar year just reading issues.&lt;/p&gt;
&lt;p&gt;So here are all the issues mentioned up to 2 steps out, in order of their creation.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/4719&quot;&gt;https://github.com/golang/go/issues/4719&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/9887&quot;&gt;https://github.com/golang/go/issues/9887&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/8893&quot;&gt;https://github.com/golang/go/issues/8893&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/11193&quot;&gt;https://github.com/golang/go/issues/11193&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/14271&quot;&gt;https://github.com/golang/go/issues/14271&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/15681&quot;&gt;https://github.com/golang/go/issues/15681&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/15734&quot;&gt;https://github.com/golang/go/issues/15734&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/15736&quot;&gt;https://github.com/golang/go/issues/15736&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/15752&quot;&gt;https://github.com/golang/go/issues/15752&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/15756&quot;&gt;https://github.com/golang/go/issues/15756&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/15799&quot;&gt;https://github.com/golang/go/issues/15799&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/15913&quot;&gt;https://github.com/golang/go/issues/15913&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/17566&quot;&gt;https://github.com/golang/go/issues/17566&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/17751&quot;&gt;https://github.com/golang/go/issues/17751&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/18981&quot;&gt;https://github.com/golang/go/issues/18981&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/19340&quot;&gt;https://github.com/golang/go/issues/19340&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/20070&quot;&gt;https://github.com/golang/go/issues/20070&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/20137&quot;&gt;https://github.com/golang/go/issues/20137&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/20512&quot;&gt;https://github.com/golang/go/issues/20512&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/20579&quot;&gt;https://github.com/golang/go/issues/20579&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/25056&quot;&gt;https://github.com/golang/go/issues/25056&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/25862&quot;&gt;https://github.com/golang/go/issues/25862&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/25999&quot;&gt;https://github.com/golang/go/issues/25999&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/26534&quot;&gt;https://github.com/golang/go/issues/26534&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/27148&quot;&gt;https://github.com/golang/go/issues/27148&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/27345&quot;&gt;https://github.com/golang/go/issues/27345&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/28314&quot;&gt;https://github.com/golang/go/issues/28314&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues/29067&quot;&gt;https://github.com/golang/go/issues/29067&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There&apos;s a lot of them ^^ some of the linked issues are near / exact matches of my search criteria &lt;a href=&quot;https://coilysiren.me/posts/2019-12-25-golang-pr-field-notes-part-1/&quot;&gt;from yesterday&lt;/a&gt;. This is a good sign, it tells me that even if I can&apos;t solve my origin issue, that I&apos;ll be able to use the context I&apos;m gaining to look into a nearby issue.&lt;/p&gt;
&lt;h2&gt;Concept discovery&lt;/h2&gt;
&lt;p&gt;Now what? Well, we need to look through the issues to look for a few things. Red flags, yellow flag, potential PRs that might solve the origin issue, and concepts I don&apos;t understand. The unknown concepts are going to be what requires the most work here, I&apos;ll need to list them out and add gain concrete understanding of them all before I can do any &quot;real work&quot;. Scanning through the issues, here&apos;s those concepts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;what is the difference between &lt;code&gt;$GOROOT/pkg&lt;/code&gt;, &lt;code&gt;$GOPATH/pkg&lt;/code&gt;? via &lt;a href=&quot;https://github.com/golang/go/issues/4719#issue-51282533&quot;&gt;#4719&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is *.a cache? via &lt;a href=&quot;https://github.com/golang/go/issues/4719#issue-51282533&quot;&gt;#4719&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what does &quot;lazy cleaning&quot; mean? via &lt;a href=&quot;https://github.com/golang/go/issues/4719#issue-51282533&quot;&gt;#4719&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what are *.a binaries? via &lt;a href=&quot;https://github.com/golang/go/issues/4719#issuecomment-66074024&quot;&gt;#4719&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what are binary-only packages? via &lt;a href=&quot;https://github.com/golang/go/issues/4719#issuecomment-66074026&quot;&gt;#4719&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is the difference between go build and go install? via &lt;a href=&quot;https://github.com/golang/go/issues/4719#issuecomment-144121444&quot;&gt;#4719&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;how could go&apos;s build system work without a pkg directory? via &lt;a href=&quot;https://github.com/golang/go/issues/4719#issuecomment-144121444&quot;&gt;#4719&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what are object files, what does it mean to link object files? via &lt;a href=&quot;https://github.com/golang/go/issues/4719#issuecomment-144125808&quot;&gt;#4719&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is gb? via &lt;a href=&quot;https://github.com/golang/go/issues/4719#issuecomment-144199351&quot;&gt;#4719&lt;/a&gt; and &lt;a href=&quot;https://github.com/golang/go/issues/14271#issue-132335366&quot;&gt;#14271&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is cgo, what does it mean to be cgo-enabled? via &lt;a href=&quot;https://github.com/golang/go/issues/4719#issuecomment-341861487&quot;&gt;#4719&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what does compilation specifically mean for go? via &lt;a href=&quot;https://github.com/golang/go/issues/4719#issuecomment-470268217&quot;&gt;#4719&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;how do binaries relate to source code? via &lt;a href=&quot;https://github.com/golang/go/issues/4719#issuecomment-470268217&quot;&gt;#4719&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Short pause. Certain people are referenced in these issues a TON. I&apos;m getting the impression that programming languages are only really written by a very small number of people (less than a dozen) who have a lot of support infrastructure (of hundreds or thousands). At any rate, back to concepts.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;how does go compilation differ from cgo compilation? via &lt;a href=&quot;https://github.com/golang/go/issues/9887#issue-57753156&quot;&gt;#9887&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;does golang support compilation to multiple different target languages, eg. C, C++, and Fortran? if so, why? via &lt;a href=&quot;https://github.com/golang/go/issues/9887#issuecomment-456852196&quot;&gt;#9887&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what does the go tool &quot;scheduling work&quot; mean? via &lt;a href=&quot;https://github.com/golang/go/issues/8893#issue-51288838&quot;&gt;#8893&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what does topological mean, and how could it lead to suboptimal parallelization? via &lt;a href=&quot;https://github.com/golang/go/issues/8893#issue-51288838&quot;&gt;#8893&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is critical path scheduling? via &lt;a href=&quot;https://github.com/golang/go/issues/8893#issue-51288838&quot;&gt;#8893&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;why is cgo compilation heavy? via &lt;a href=&quot;https://github.com/golang/go/issues/8893#issuecomment-73424189&quot;&gt;#8893&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another aside -- I think I just spotted &lt;a href=&quot;https://github.com/golang/go/issues/8893#issuecomment-100422418&quot;&gt;a remnant&lt;/a&gt; of the point at which golang switched to compiling itself. A quick google and I now know that go was originally written &lt;a href=&quot;https://docs.google.com/document/d/1P3BLR31VA8cvLJLfMibSuTdwTuF7WWLux71CYD0eeD8/preview?pli=1&quot;&gt;in C&lt;/a&gt;. Back to concepts!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;what is asm compilation? via &lt;a href=&quot;https://github.com/golang/go/issues/8893#issuecomment-101070036&quot;&gt;#8893&lt;/a&gt; and &lt;a href=&quot;https://github.com/golang/go/issues/17566#issuecomment-256156430&quot;&gt;#17566&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is SSA? via &lt;a href=&quot;https://github.com/golang/go/issues/8893#issuecomment-134458436&quot;&gt;#8893&lt;/a&gt; and &lt;a href=&quot;https://github.com/golang/go/issues/15736#issue-155612156&quot;&gt;#15736&lt;/a&gt; and &lt;a href=&quot;https://github.com/golang/go/issues/17566#issuecomment-256149722&quot;&gt;#17566&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;how do you avoid recompiling a package every time you run tests? via &lt;a href=&quot;https://github.com/golang/go/issues/11193#issuecomment-229522796&quot;&gt;#11193&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what does it mean to compile versus to link? via &lt;a href=&quot;https://github.com/golang/go/issues/14271#issue-132335366&quot;&gt;#14271&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is cmd/go? via &lt;a href=&quot;https://github.com/golang/go/issues/14271#issue-132335366&quot;&gt;#14271&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is bazel? via &lt;a href=&quot;https://github.com/golang/go/issues/14271#issuecomment-181840701&quot;&gt;#14271&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Did you know -- if you&apos;re spending hours reading GitHub issues in the winter, you can concurrently be applying lotion to your dry skin? It&apos;s true! Anyways.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;what are go:cgo_import_dynamic directives? via &lt;a href=&quot;https://github.com/golang/go/issues/15681#issuecomment-239866168&quot;&gt;#15681&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is the relationship between golang and C? what is the relationship between c compilations and the linker? via &lt;a href=&quot;https://github.com/golang/go/issues/15681#issuecomment-240319812&quot;&gt;#15681&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;how do .a files relate to .o files? via &lt;a href=&quot;https://github.com/golang/go/issues/15681#issuecomment-380967328&quot;&gt;#15681&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what are .c files? via &lt;a href=&quot;https://github.com/golang/go/issues/15681#issuecomment-380967328&quot;&gt;#15681&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;does golang use gcc? via &lt;a href=&quot;https://github.com/golang/go/issues/15681#issuecomment-380967328&quot;&gt;#15681&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is the difference between export data and machine code? via &lt;a href=&quot;https://github.com/golang/go/issues/15734#issue-155608860&quot;&gt;#15734&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what are inlined functions? via &lt;a href=&quot;https://github.com/golang/go/issues/15734#issue-155608860&quot;&gt;#15734&lt;/a&gt; and &lt;a href=&quot;https://github.com/golang/go/issues/17566#issue-184877974&quot;&gt;#17566&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what does it mean to walk a function? via &lt;a href=&quot;https://github.com/golang/go/issues/15734#issue-155608860&quot;&gt;#15734&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is a semaphore? via &lt;a href=&quot;https://github.com/golang/go/issues/15734#issuecomment-220172384&quot;&gt;#15734&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is a VFS? via &lt;a href=&quot;https://github.com/golang/go/issues/15734#issuecomment-220172384&quot;&gt;#15734&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;how do processes communicate with each other? via &lt;a href=&quot;https://github.com/golang/go/issues/15734#issuecomment-220172384&quot;&gt;#15734&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;how does export data relate to .a files? via &lt;a href=&quot;https://github.com/golang/go/issues/15734#issuecomment-220173676&quot;&gt;#15734&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is the relationship between export data and objects? via &lt;a href=&quot;https://github.com/golang/go/issues/15734#issuecomment-339211682&quot;&gt;#15734&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is cmd/go? via &lt;a href=&quot;https://github.com/golang/go/issues/15736#issue-155612156&quot;&gt;#15734&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Something I&apos;ve noticed while writing this, is that the detail amount of time I can spend reading before encountering a new unknown concept is increasing. I&apos;m also gaining some understanding of the unknown concepts simply via seeing them mentioned in context, eg. without needing to look up their definitions. That said, the next step will definitely be to look up definitions ^^. Alright, back to concepts.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;how are go&apos;s cmds organized, eg. cmd/go vs cmd/compile, etc? via &lt;a href=&quot;https://github.com/golang/go/issues/15736#issuecomment-220181188&quot;&gt;#15736&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is typechecking? via &lt;a href=&quot;https://github.com/golang/go/issues/15756#issue-155838129&quot;&gt;#15756&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is escape analysis? via &lt;a href=&quot;https://github.com/golang/go/issues/15756#issue-155838129&quot;&gt;#15756&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what steps are required for compilation? via &lt;a href=&quot;https://github.com/golang/go/issues/15756#issue-155838129&quot;&gt;#15756&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what does it mean to &quot;rebuild even when target is up to date&quot; eg. what is the target of builds? via &lt;a href=&quot;https://github.com/golang/go/issues/15799#issue-156283605&quot;&gt;#15799&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is yyerrorn? via &lt;a href=&quot;https://github.com/golang/go/issues/15913#issue-157781645&quot;&gt;#15913&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is a gc node? via &lt;a href=&quot;https://github.com/golang/go/issues/17566#issue-184877974&quot;&gt;#17566&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what are OKEY and OAPPEND nodes? via &lt;a href=&quot;https://github.com/golang/go/issues/17566#issue-184877974&quot;&gt;#17566&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what does it mean to inline during compilation? via &lt;a href=&quot;https://github.com/golang/go/issues/17566#issue-184877974&quot;&gt;#17566&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what are bounds checks? via &lt;a href=&quot;https://github.com/golang/go/issues/17566#issue-184877974&quot;&gt;#17566&lt;/a&gt; and &lt;a href=&quot;https://github.com/golang/go/issues/25862#issue-331945781&quot;&gt;#25862&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what does &quot;generating code&quot; mean in the context of compilation? via &lt;a href=&quot;https://github.com/golang/go/issues/17566#issue-184877974&quot;&gt;#17566&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what would be the benefit of inlining performance critical functions? via &lt;a href=&quot;https://github.com/golang/go/issues/17566#issue-184877974&quot;&gt;#17566&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is the cost of inlining a function? via &lt;a href=&quot;https://github.com/golang/go/issues/17566#issue-184877974&quot;&gt;#17566&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is a pragma? via &lt;a href=&quot;https://github.com/golang/go/issues/17566#issuecomment-256139958&quot;&gt;#17566&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is closure conversion? via &lt;a href=&quot;https://github.com/golang/go/issues/17566#issuecomment-256149722&quot;&gt;#17566&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what does it mean to export a function? via &lt;a href=&quot;https://github.com/golang/go/issues/17566#issuecomment-262118244&quot;&gt;#17566&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is zero-cost control flow abstraction? via &lt;a href=&quot;https://github.com/golang/go/issues/17566#issuecomment-385211150&quot;&gt;#17566&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is an intrinsic? via &lt;a href=&quot;https://github.com/golang/go/issues/17566#issuecomment-417111586&quot;&gt;#17566&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I just went through an entire (long) issue without noticing any new concepts! Exciting.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;what is the binary export format? via &lt;a href=&quot;https://github.com/golang/go/issues/20070#issuecomment-296292391&quot;&gt;#20070&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is a package archive? via &lt;a href=&quot;https://github.com/golang/go/issues/20579#issue-233602713&quot;&gt;#20579&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what are registers? via &lt;a href=&quot;https://github.com/golang/go/issues/25999#issuecomment-399196221&quot;&gt;#25999&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what does it mean to spill a register? via &lt;a href=&quot;https://github.com/golang/go/issues/25999#issuecomment-399199303&quot;&gt;#25999&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is the stack, and how does it relate to optimization? via &lt;a href=&quot;https://github.com/golang/go/issues/25999#issuecomment-400841088&quot;&gt;#25999&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what does it mean to preempt a goroutine with signals? via &lt;a href=&quot;https://github.com/golang/go/issues/25999#issuecomment-400843932&quot;&gt;#25999&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what are runes? via &lt;a href=&quot;https://github.com/golang/go/issues/27148#issuecomment-415131187&quot;&gt;#27148&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is a goroutine? via &lt;a href=&quot;https://github.com/golang/go/issues/27345#issue-355263332&quot;&gt;#27345&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is an unbuffered channel? via &lt;a href=&quot;https://github.com/golang/go/issues/27345#issuecomment-419254790&quot;&gt;#27345&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is BCE? via &lt;a href=&quot;https://github.com/golang/go/issues/28314&quot;&gt;#28314&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is morestack? via &lt;a href=&quot;https://github.com/golang/go/issues/29067&quot;&gt;#29067&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is a branch target buffer, what are backwards versus forward branches? via &lt;a href=&quot;https://github.com/golang/go/issues/29067#issuecomment-443587472&quot;&gt;#29067&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;what is icache? via &lt;a href=&quot;https://github.com/golang/go/issues/29067#issuecomment-443887583&quot;&gt;#29067&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wow that was exhausting&lt;/h2&gt;
&lt;p&gt;So here&apos;s what we did:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;dropped into the golang issue tracker with a specific learning goal in mind&lt;/li&gt;
&lt;li&gt;found a specific issue, and walked through all the related issue for concepts that need to be learned&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And here&apos;s what we will do next:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;define and understand most (not all) of the concepts, focusing on hotspots&lt;/li&gt;
&lt;li&gt;return to the original issue, and determine if we have enough information to create a fix (likely yes, 90% confidence)&lt;/li&gt;
&lt;li&gt;start working on a fix&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&apos;ll come later! For today, I&apos;m headed back to video games.&lt;/p&gt;
&lt;h2&gt;Up next&lt;/h2&gt;
&lt;p&gt;===&gt; &lt;a href=&quot;https://coilysiren.me/posts/2019-12-29-golang-pr-field-notes-part-3/&quot;&gt;part 3 is this way&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Golang PR Field Notes (Part 1)]]></title><description><![CDATA[I'm writing these notes as a part of my grand quest to make a performance improvement to Golang!]]></description><link>https://coilysiren.me/posts/golang-pr-notes-1/</link><guid isPermaLink="false">https://coilysiren.me/posts/golang-pr-notes-1/</guid><dc:creator><![CDATA[@coilysiren]]></dc:creator><pubDate>Wed, 25 Dec 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Identifying an issue&lt;/h2&gt;
&lt;p&gt;I started with of the following search:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;open issues&lt;/li&gt;
&lt;li&gt;that need a fix&lt;/li&gt;
&lt;li&gt;that don&apos;t have a CL (eg. &lt;a href=&quot;https://news.ycombinator.com/item?id=20891103&quot;&gt;a changelist&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;that aren&apos;t planned to be fixed by the go team&lt;/li&gt;
&lt;li&gt;about toolspeed&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/golang/go/issues?utf8=%E2%9C%93&amp;#x26;q=is%3Aopen+label%3AToolSpeed+label%3ANeedsFix+NOT+%22golang.org%2Fcl%22+milestone%3AUnplanned+&quot;&gt;From this url&lt;/a&gt;. There were a few considerations that went into that search. In my experience with open source, and also under the guidance of go&apos;s &lt;a href=&quot;https://golang.org/doc/contribute.html#before_contributing&quot;&gt;contributing guide&lt;/a&gt;, I determined that &lt;em&gt;&quot;open issues that will take a fix, but no fix is planned&quot;&lt;/em&gt; is the best path for contribution. They essentially say &lt;em&gt;&quot;we will take a fix that looks like this, if anyone drops in to create it&quot;&lt;/em&gt;. The golang issue tracker separates &quot;backlog&quot; from &quot;unplanned&quot;, which I learned about here =&gt; &lt;a href=&quot;https://github.com/golang/go/issues/34376&quot;&gt;https://github.com/golang/go/issues/34376&lt;/a&gt;. I assume that a backlog issue may have someone on the golang team drop in and sweep it out from under me, whereas unplanned issues are free game essentially indefinitely. I&apos;ve had a variant of that happen to me before, where I created a PR for django and then a maintainer decided to &lt;a href=&quot;https://github.com/django/django/pull/9016#issuecomment-373777203&quot;&gt;take some of my work and start their own PR&lt;/a&gt;. So, I&apos;m painfully allergic to that happening again here.&lt;/p&gt;
&lt;p&gt;The focus on toolspeed is inspired by my learning goals, I go into that briefly on twitter here =&gt; &lt;a href=&quot;https://twitter.com/coilysiren/status/1209628089346490368&quot;&gt;https://twitter.com/coilysiren/status/1209628089346490368&lt;/a&gt;. There&apos;s another label that fits my learning goal, the &quot;performance&quot; label. Here&apos;s both the toolspeed and performance searches, mostly for my future reference:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues?utf8=%E2%9C%93&amp;#x26;q=is%3Aopen+label%3AToolSpeed+label%3ANeedsFix+NOT+%22golang.org%2Fcl%22+milestone%3AUnplanned+&quot;&gt;toolspeed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/issues?q=is%3Aopen+label%3ANeedsFix+NOT+%22golang.org%2Fcl%22+milestone%3AUnplanned+label%3APerformance&quot;&gt;performance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Short aside on labeling&lt;/h2&gt;
&lt;p&gt;If you can&apos;t tell from my methodology here, accurately labeling issues is critically import for new contributors. When I first drop into a repo, &lt;a href=&quot;https://github.com/golang/go/labels&quot;&gt;the labels&lt;/a&gt; are the first thing I look at. There&apos;s a lot of new information to ingest when entering a repo, and often the labels are the best &quot;front door&quot;.&lt;/p&gt;
&lt;p&gt;This makes accurately labeling issues a fairly high priority concern for repository maintainers, in my opinion. I was very happy to see &lt;a href=&quot;https://discuss.python.org/t/proposal-create-bug-triage-team-on-github/992&quot;&gt;a cpython proposal&lt;/a&gt; by &lt;a href=&quot;https://twitter.com/mariatta&quot;&gt;Mariatta&lt;/a&gt; on creating a &quot;bug triage&quot; role. Essentially the entire job of that role is to apply labels. I would totally apply to that role, if contributing to cpython looked like it would helpful for my career. But it&apos;s looking like golang is going to be where my interests lie, so that&apos;s why you&apos;re reading a post about golang.&lt;/p&gt;
&lt;h2&gt;Exploring the issue&lt;/h2&gt;
&lt;p&gt;So, with the previously mentioned search as my guide, I went for the first issue that looked the most approachable. &quot;Approachable&quot; here is a subjective definition, it translates roughly to &quot;do I feel capable of doing this&quot;. The issue I picked was:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/golang/go/issues/15752&quot;&gt;https://github.com/golang/go/issues/15752&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;cmd/go: only rebuild dependent packages when export data has changed&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;From the title, I determined that the issue relies on core understanding of two concepts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;dependent packages (which I understand)&lt;/li&gt;
&lt;li&gt;export data (which is unknown to me)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This prompts our focus to revolve around the &quot;export data&quot; concept. There is a particular dependency in this issue on &quot;the export data changing&quot; which is a pre-requisite for making any enhancements based on that fact. That is, in order to create any logic that relies on diffs in the export data, we have to be able to fully trust that diffs in the export data are sufficiently reliable to make decisions. We can state that fact without first even having an understanding of what &quot;export data&quot; is.&lt;/p&gt;
&lt;p&gt;The export data reliability point comes up in the issues description, specifically in this line&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Not sure how often it happens that code changes don&apos;t impact export data (or how cheap that is to detect), but when it does happen, that could save a bunch of computation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;From this context I&apos;m able to determine that &quot;export data&quot; means something like &quot;metadata about a function&quot;. I assume that the export data contains some basic information about a function of package, such as last edited time / file size / function signatures / etc.&lt;/p&gt;
&lt;p&gt;There&apos;s one part here that&apos;s particularly important to emphasize&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Not sure how often it happens that code changes don&apos;t impact export data&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So golang teams&apos; current understanding is that it&apos;s unknown if the export data is currently reliable enough to make decisions based on it. At this point, I&apos;m starting to reframe the issue into two separate tasks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;only rebuild dependent packages when export data has changed &lt;em&gt;(the original task)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;change the export data for a dependent package whenever that package needs to be rebuilt &lt;em&gt;(a derived task from the above)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This surfaces another question, specifically: &lt;em&gt;&lt;strong&gt;what does it mean to build a dependent package, and when do we know that a dependent package needs to be rebuilt?&lt;/strong&gt;&lt;/em&gt; Being able to answer that question is a pre-requisite to writing code for the conditional rebuilding of dependent packages. This creates a set of subtasks, specifically&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;change the export data during (case 1)&lt;/li&gt;
&lt;li&gt;change the export data during (case 2)&lt;/li&gt;
&lt;li&gt;etc...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Further comments give more information about the export data concept&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;the plan would be to do something like check the SHA of the export data. If that hasn&apos;t changed, then the &lt;strong&gt;downstream compilation won&apos;t change&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;p&gt;Yeah, I was just thinking that you&apos;d store the SHA1 of the export data of&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;all imported packages in the .a file and only recompile if any of them had
changed. No sense trying to be too sophisticated.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I don&apos;t know often this would be useful. Certainly sometimes (especially in&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;a &lt;strong&gt;&quot;add more and more debug prints&quot;&lt;/strong&gt; cycle)&lt;/p&gt;
&lt;p&gt;Emphasis mine. So part of the goal is to avoid rebuilding a dependent package if you&apos;ve made trivial internal changes to it, like for example adding some debugging prints.&lt;/p&gt;
&lt;p&gt;Further down the thread I&apos;m seeing several people mention that they&apos;re working on some portion of this (&lt;a href=&quot;https://github.com/golang/go/issues/15752#issuecomment-220458227&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;https://github.com/golang/go/issues/15752#issuecomment-313510033&quot;&gt;2&lt;/a&gt;) which gives me some pause. But those comments are from years ago, so I believe I&apos;m in the clear.&lt;/p&gt;
&lt;p&gt;Another comment by the author further reinforces the direction I believe this should be approached from&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Is this now just (“just”) a matter of only hashing the export data when determining whether object files are stale?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;specifically my note about &lt;em&gt;change the export data during...&lt;/em&gt; above. This comment surfaces some new questions, specifically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&lt;strong&gt;what are object files?&lt;/strong&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;strong&gt;how do we know when object files are stale?&lt;/strong&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;strong&gt;how do we change the export data when object files are stale?&lt;/strong&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The final comment in this issue is a wildcard for me, it says&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I think it&apos;s a little bit more than that because you still need to re-link any executables?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This statement is hard for me to parse, and raises several questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&lt;strong&gt;how do executables relate to needing to rebuild dependent packages?&lt;/strong&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;strong&gt;what does it mean to re-link executables?&lt;/strong&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I know that executables are a type of asset produced by a build process. Specifically I believe that &lt;code&gt;go build ...&lt;/code&gt; produces executables. It was my understanding that go build produced a single &lt;a href=&quot;https://en.wikipedia.org/wiki/Static_library#Linking_and_loading&quot;&gt;statically linked&lt;/a&gt; executable, but I now believe that understanding is incorrect. My new understanding is that &lt;code&gt;go build ...&lt;/code&gt; possibly creates multiple executables, perhaps one for each package? If you were conditionally rebuilding executables based on export data, then you would end up with multiple executables where some of them are outdated and need to be pointed to new links? I&apos;ll be sure to investigate this.&lt;/p&gt;
&lt;h2&gt;Next steps&lt;/h2&gt;
&lt;p&gt;So far we&apos;ve identified the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;there is a problem I can solve here&lt;/li&gt;
&lt;li&gt;the desired solution has some performance impact&lt;/li&gt;
&lt;li&gt;I don&apos;t currently possess all the knowledge I need to solve them problem, but the amount of knowledge that I need to gain feels reasonable&lt;/li&gt;
&lt;li&gt;there is some active work on this issue, but not so much active work that my contributions would be invalidated&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In summary, there&apos;s work to do! My next step is to double down on investigating this issue, and gain any surrounding context that I might need.&lt;/p&gt;
&lt;h2&gt;Up next&lt;/h2&gt;
&lt;p&gt;===&gt; &lt;a href=&quot;https://coilysiren.me/posts/2019-12-26-golang-pr-field-notes-part-2/&quot;&gt;part 2 is this way&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[The Maintainer - Aspect of Code Janitor]]></title><description><![CDATA[The code janitor is a servant leader of the codebase.]]></description><link>https://coilysiren.me/posts/code-janitor/</link><guid isPermaLink="false">https://coilysiren.me/posts/code-janitor/</guid><dc:creator><![CDATA[@coilysiren]]></dc:creator><pubDate>Sat, 27 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The code janitor is a servant leadership of a codebase. They use their skills primarily to increase the software engineering velocity of the team as a whole / increase the stability of the code. Their roles are such:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Encourage continually better code pratices&lt;/strong&gt; in your coworkers. If they mention writing code they haven&apos;t pushed to a remote branch, get them to push that branch! If they push code without tests, get them to write tests! Continually iterate on best practices for branching and test coverage. Good branching enables good review and helps avoid duplication of work. The benefits of good tests are a subject for another post ^^. &quot;Better code practices&quot; is a continously moving target, so the code janitor must also be tuned into a technical community to stay up to date on them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Proactively review =&gt; merge&lt;/strong&gt; the pull requests of coworkers / community members. Coworker&apos;s PRs should all be reviewed at least every 24 hours. Community member&apos;s PRs should be reviewed all be reviewed at least every 7 days. If you&apos;re approaching a time limit and aren&apos;t sure what feedback to give, really push yourself to surface why a pull request shouldn&apos;t be merged.  Continuous proactive reviews are a powerful tool for surfacing people&apos;s blockers.&lt;/p&gt;
&lt;p&gt;As a special case for reviews and merges, the code janitor should &lt;strong&gt;maintain a requirements bot&lt;/strong&gt;. The benefits of a requirements bot are described in &lt;a href=&quot;https://coilysiren.me/post/requirements-maint&quot;&gt;another post&lt;/a&gt;. The timelines for requirements updates should be relative to the &lt;a href=&quot;https://semver.org/&quot;&gt;semver&lt;/a&gt; range of the update, and the type of update. For example, security patches should be merged within 24 hours.&lt;/p&gt;
&lt;p&gt;Tangentially related to maintaining your current set of requirements, is &lt;strong&gt;watching for competitor libraries&lt;/strong&gt;. When your team&apos;s use of a tool is non-standard / non-ideal, its often a good target for being replaced by a competitor. For the majority of your libraries this won&apos;t be an option, but there&apos;s some cases where its easily possible. Switching my team&apos;s &lt;a href=&quot;https://github.com/ambv/black&quot;&gt;code formatter&lt;/a&gt; and &lt;a href=&quot;https://github.com/mochajs/mocha&quot;&gt;test framework&lt;/a&gt; are two examples of this. A good code janitor developes a sense for where / when these switches can happen, and is keyed into the communities where these libraries are created.&lt;/p&gt;
&lt;p&gt;Watch how your coworkers are interacting with the linter config. Ask your coworkers how they are interacting with the linter config! Be very receptive to &lt;strong&gt;changing the linter config&lt;/strong&gt; over time, as personal styles shift. Definitely change linter config whenever you get a new coworker of at least median skill level, they know what works well for them. The gold standard is config that minizes the time programmers spend thinking about formatting, while also completely preventing the possibility of Formatting Wars.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Driving refactor plans&lt;/strong&gt;, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;renames&lt;/li&gt;
&lt;li&gt;merging projects / splitting projects apart&lt;/li&gt;
&lt;li&gt;open sourcing all of / parts of a project&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Identifying technical debt&lt;/strong&gt;, and surfacing the relative priority of reducing that debt to team managers. Issues of technical debt can often be hard to communicate, so part of this work should involve configuring tools that assist in objective code analysis. This very related to driving refactor plans.&lt;/p&gt;
&lt;p&gt;There&apos;s more to say here, but this post is long and the day is short. I hope this helps you become a better project maintainer!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Heroku + Django Pipeline + Sass]]></title><description><![CDATA[A currently existing django project, hosted on heroku, with a bunch of css]]></description><link>https://coilysiren.me/posts/heroku-django-sass/</link><guid isPermaLink="false">https://coilysiren.me/posts/heroku-django-sass/</guid><dc:creator><![CDATA[@coilysiren]]></dc:creator><pubDate>Thu, 30 Mar 2017 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;What you have&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A currently existing django project, hosted on heroku, with a bunch of css&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;What you want&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;to have your css compiled from sass&lt;/li&gt;
&lt;li&gt;for that compilation to be done with ruby, during the heroku build&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;How you get it&lt;/h3&gt;
&lt;p&gt;heroku setup&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# bash

heroku buildpacks:add heroku/ruby --index 1 --app $APP_NAME
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;django pipeline setup&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;# config/settings/base.py

# snipped to only the relevant bit, see django pipeline&apos;s docs for the rest
PIPELINE = {
    &apos;COMPILERS&apos;: (
        &apos;pipeline.compilers.sass.SASSCompiler&apos;,
    ),
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;sass setup&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ruby&quot;&gt;# Gemfile

source &quot;https://rubygems.org&quot;
ruby &apos;2.3.3&apos;

gem &quot;sass&quot;
gem &quot;susy&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Ok but that didn&apos;t work&lt;/h4&gt;
&lt;p&gt;or at least it didn&apos;t for me, I got this (formatted for readability) error&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;pipeline.exceptions.CompilerError: &amp;#x3C;filter object at 0x1337&gt; exit code 1
b&quot;/tmp/DIR/vendor/ruby-2.3.3/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require&apos;:
    cannot load such file -- bundler/setup (LoadError)
    from /tmp/DIR/vendor/ruby-2.3.3/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require&apos;
    from /tmp/DIR/vendor/bundle/bin/sass:15:in `&amp;#x3C;main&gt;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From that message, it occurred to me that maybe python wasn&apos;t calling the right &lt;code&gt;sass&lt;/code&gt;. Which inspired the following &quot;fix&quot;&lt;/p&gt;
&lt;h4&gt;How you get it, but for real this time&lt;/h4&gt;
&lt;p&gt;Install your gems to whatever &lt;code&gt;ruby&lt;/code&gt; / &lt;code&gt;sass&lt;/code&gt; the python buildpack is calling&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# bin/pre_compile
gem install bundler
bundle install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(&lt;code&gt;pre_compile&lt;/code&gt; is a hook for the python buildpack)&lt;/p&gt;
&lt;p&gt;Does this mean &lt;code&gt;heroku buildpacks:add heroku/ruby --index 1&lt;/code&gt; is unused? Someone investigate this and &lt;a href=&quot;https://twitter.com/coilysiren&quot;&gt;let me know&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Get this fixed for good!&lt;/h3&gt;
&lt;p&gt;One of the devs of the projects below will know what&apos;s up&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jazzband/django-pipeline&quot;&gt;https://github.com/jazzband/django-pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/heroku/heroku-buildpack-ruby&quot;&gt;https://github.com/heroku/heroku-buildpack-ruby&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/heroku/heroku-buildpack-python&quot;&gt;https://github.com/heroku/heroku-buildpack-python&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But if you want a fix for your project, the &lt;code&gt;pre_compile&lt;/code&gt; script will work fine&lt;/p&gt;</content:encoded></item></channel></rss>