<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Cameron Pavey</title>
    <description>The latest articles on DEV Community by Cameron Pavey (@cpave3).</description>
    <link>https://dev.to/cpave3</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F448009%2Fa114b4b6-e1c7-4dc5-8128-f10d4a45d888.jpeg</url>
      <title>DEV Community: Cameron Pavey</title>
      <link>https://dev.to/cpave3</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cpave3"/>
    <language>en</language>
    <item>
      <title>AI Agents Are Not Users: Building an Identity Model That Reflects That</title>
      <dc:creator>Cameron Pavey</dc:creator>
      <pubDate>Sat, 10 Oct 2026 22:15:00 +0000</pubDate>
      <link>https://dev.to/auth0/ai-agents-are-not-users-building-an-identity-model-that-reflects-that-1mfp</link>
      <guid>https://dev.to/auth0/ai-agents-are-not-users-building-an-identity-model-that-reflects-that-1mfp</guid>
      <description>&lt;p&gt;In April 2026, an &lt;a href="https://www.theguardian.com/technology/2026/apr/29/claude-ai-deletes-firm-database" rel="noopener noreferrer"&gt;AI coding agent deleted the entire production database of PocketOS&lt;/a&gt;, a B2B platform used by car rental companies, along with every volume-level backup, in nine seconds. The agent was Cursor, running on a leading frontier model, configured with explicit safety rules its founder later described as "exactly what these vendors tell developers to do." It was working on a routine staging task when it encountered a credential mismatch, found a token with sufficient permissions, and issued a single GraphQL mutation that wiped everything. &lt;/p&gt;

&lt;p&gt;The system prompt told it not to do anything destructive without approval, but system prompts are advisory, not enforced. As the post-mortem makes clear, the deeper architectural failure was giving a non-deterministic agent the same access a human operator would have, and trusting natural-language instructions to constrain what it did with that access.&lt;/p&gt;

&lt;p&gt;The agent did not behave like a human user. It ran continuously and ignored explicit instructions. It also did not behave like a backend service, as it had no fixed code path and no auditor-readable workflow. The identity layer underneath it had to choose one of those two shapes, because those are the shapes our identity infrastructure offers. It chose wrong, but there was no way for it to choose right, because the right shape did not really exist.&lt;/p&gt;

&lt;p&gt;This article makes the case that AI agents need to be treated as first-class identities in their own right, distinct from human users and from traditional machine identities. That case covers why the traditional models break down, what a first-class agent identity consists of, how lifecycle management has to change, why authorization has to be evaluated at runtime against the request rather than baked into roles at provisioning time, and what observability requirements emerge. The goal is not to build a new identity primitive from scratch, but to extend your existing infrastructure to handle a class of actor it was not designed for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the Legacy Models Break Down
&lt;/h2&gt;

&lt;p&gt;OAuth delegation gives the agent the same authority as the user. When a user grants an agent access to their CRM with the scopes they themselves hold, the agent can do anything the user can, across every future interaction, regardless of what it is being asked to do in any specific moment. The intent is selective delegation, but OAuth cannot express it at that granularity. Saying "let the agent see contact details for accounts I'm currently working on, but not anything in the legal holding queue" requires elaborate scope engineering that few products expose.&lt;/p&gt;

&lt;p&gt;Service accounts have the opposite problem. The agent gets its own credential, but that credential is typically static, broadly scoped, and disconnected from any user context. When something goes wrong, the audit log shows that the service account did it, which is almost no information at all. There is no way to trace back through the chain of delegation, no way to see which user request triggered the action, and no way to revoke a single agent run without revoking the entire service account.&lt;/p&gt;

&lt;p&gt;Both models share four failure modes that worsen as agent usage scales:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Over-permissioning:&lt;/strong&gt; The credential carries broader authority than the agent needs for the task in front of it, because scopes are typically defined at the resource level (&lt;code&gt;billing:write&lt;/code&gt;) rather than the capability level (&lt;code&gt;billing.refund.issue_under_50_usd&lt;/code&gt;).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blast radius:&lt;/strong&gt; A compromise of the agent process, its memory, or its logs exposes a credential that grants standing access to production systems.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lack of behavioral context:&lt;/strong&gt; The identity layer cannot distinguish between "the agent is summarizing tickets" and "the agent is issuing refunds" because both operations present the same broadly-scoped credential that authorizes both actions.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credential sprawl:&lt;/strong&gt; Every new tool the agent integrates with means another credential to provision, store, rotate, and audit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All four failure modes existed before AI agents and have been seen in incidents involving traditional automation. However, agents make them worse for two main reasons. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The gap between the credential's authority and the work the agent is doing at any specific moment is much wider than it is for traditional automation.
&lt;/li&gt;
&lt;li&gt;The agent's behavior is &lt;a href="https://auth0.com/blog/ai-agents-have-two-souls-you-control-only-one" rel="noopener noreferrer"&gt;non-deterministic&lt;/a&gt; in a way that conflicts with the assumptions baked into both models. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A service account is safe partly because the code calling it is fixed. An agent's "code" is a tool-calling loop steered by an LLM interpreting natural language input that may itself be untrusted. When you can no longer rely on the determinism of an automation's code, the credential's scoping becomes one of the few remaining constraints on what the agent can do.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a First-Class Agent Identity Looks Like
&lt;/h2&gt;

&lt;p&gt;A first-class agent identity is more than a service account with a different label. It has its own lifecycle, permissions, policy bindings, and audit trail. It is an identity primitive that is structurally distinct from both users and traditional machine identities.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://openfga.dev/" rel="noopener noreferrer"&gt;OpenFGA&lt;/a&gt;, the CNCF authorization engine that underpins Auth0's Fine-Grained Authorization product, formalizes this in its &lt;a href="https://openfga.dev/docs/modeling/agents" rel="noopener noreferrer"&gt;authorization patterns for agents&lt;/a&gt;. Agents are modeled as &lt;a href="https://auth0.com/blog/why-ai-agents-need-their-own-permission-model" rel="noopener noreferrer"&gt;first-class principals&lt;/a&gt; that participate in the same permission hierarchy as users, with their own type in the authorization model and their own relationships to resources, but with permissions scoped to capabilities and bound to tasks rather than inherited as-is from a user role.&lt;/p&gt;

&lt;p&gt;Specifically, a first-class agent identity has four properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A directory identifier&lt;/strong&gt; distinct from any user or service account, with metadata describing the agent type, the agent definition version, and ideally, who is accountable for its behavior.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scoped permissions&lt;/strong&gt; defined in business terms, separate from any user's permissions even when the agent is acting on a user's behalf.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delegation context&lt;/strong&gt; recording both the agent as actor and the user as subject when the agent acts on someone's behalf. Downstream systems see "agent X, acting on behalf of user Y, with delegated authority Z" rather than just "service account did a thing."
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A dedicated audit trail&lt;/strong&gt; attributing every action to the specific agent identity with the delegation chain intact, separate from the logs the underlying tools generate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A backend service has a static workflow and code paths that an auditor can read, so a broadly scoped credential is constrained by what the code does. An agent has none of these constraints. Its workflow is determined at runtime by an LLM, and its code path is a tool-calling loop that takes in untrusted input. The credential has to do the work that the code is not doing, and it has to do that work at the request level, not at provisioning time. OAuth 2.0 &lt;a href="https://datatracker.ietf.org/doc/html/rfc8693" rel="noopener noreferrer"&gt;Token Exchange (RFC 8693)&lt;/a&gt; was designed to support this kind of dynamic, request-scoped delegation, and it is the foundation that Auth0's Token Vault builds on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lifecycle Management for Ephemeral Actors
&lt;/h2&gt;

&lt;p&gt;User accounts live for years, and service accounts live for the lifetime of the service. Agent identities live somewhere in between, often lower down the spectrum. A long-running agent that handles support tickets might persist for the lifetime of the deployment. A task-scoped agent that runs once to answer a single user query might exist only for the duration of that task.&lt;/p&gt;

&lt;p&gt;Provisioning is rarely interactive. Consent happens when a user authorizes an application that uses agents, and the agent identities are created on demand by that application. Provisioning has to be programmatic, idempotent, and tied to the agent definition rather than a human approval workflow. &lt;/p&gt;

&lt;p&gt;Execution-context credentials should not be the same credentials that establish the agent's identity. The identity establishes who the agent is and what it could in principle do, while the execution-context credential is a short-lived token, scoped to the operation in front of the agent, that authorizes a particular call. This is the pattern Auth0's Token Vault implements at the OAuth layer: the agent does not hold the long-lived refresh token for a connected service, and instead requests a &lt;a href="https://auth0.com/blog/secure-ai-stop-managing-api-keys-by-hand" rel="noopener noreferrer"&gt;just-in-time access token through OAuth 2.0 Token Exchange&lt;/a&gt;. If the agent is compromised, the only credential exposed is whatever short-lived token it currently holds.&lt;/p&gt;

&lt;p&gt;The diagram below shows what this flow looks like in practice. There are two phases. Once, when the application is first authorized, the user grants consent and the identity provider stores the long-lived refresh token in Token Vault, where it stays. Then, for each operation the agent needs to perform, it requests a short-lived access token scoped to that specific operation. The identity provider evaluates the request against policy, returns a token that lasts minutes rather than days, and the agent uses it for a single tool call before discarding it. If the agent is compromised, the attacker inherits only whatever short-lived token happens to be in memory, and revoking the connection at the identity provider invalidates all future token exchanges immediately.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffeaxp31k09o5vappx4iw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffeaxp31k09o5vappx4iw.png" alt="Sequence diagram showing the provisioning of agent credentials" width="800" height="911"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Suspension and decommissioning also have to be easy, fast, and accurate. Recall the PocketOS incident: when the agent went off the rails, there was no equivalent of "revoke the agent's database write capability for the next ten minutes while we figure out what just happened." The credential it was using gave it standing authority, and standing authority can only be revoked at the level of the whole credential. Centralizing token issuance through a single identity provider makes finer-grained revocation possible, since revoking a connection invalidates future token exchanges immediately while leaving the rest of the system running.&lt;/p&gt;

&lt;h2&gt;
  
  
  Policy-Driven Authorization
&lt;/h2&gt;

&lt;p&gt;Static role assignment is the traditional model for user and service identity: an actor has a role, the role grants permissions, and permissions are checked against actions. This works fine when behavior is predictable, but is inadequate for agents. An agent might have access to a wide set of capabilities in principle, but in any given task, it should only exercise a narrow subset, determined by the task itself.&lt;/p&gt;

&lt;p&gt;A better model is to bind authorization to behavior rather than to identity, evaluated at runtime against the specific request. There are two relevant patterns here. Attribute-based access control (ABAC) takes contextual attributes into account: the user the agent is acting on behalf of, the resource, the time, the action, and any task parameters. &lt;a href="https://auth0.com/blog/relationship-based-access-control-rebac/" rel="noopener noreferrer"&gt;Relationship-based access control (ReBAC)&lt;/a&gt; goes further, expressing authorization in terms of relationships between entities. This makes it natural to write rules like "an agent can read contact details for accounts where it has an active assigned ticket," because the relationship is a first-class concept in the model rather than something encoded through scope engineering.&lt;/p&gt;

&lt;p&gt;ABAC alone is enough for most teams. If your authorization rules can be expressed as "this attribute equals that attribute," you may not need the full relational graph. ReBAC pays for itself when you're managing thousands of resource-relationship pairs across a multi-tenant system, where the cost of expressing those rules in any other model becomes prohibitive. For a small team with a single-tenant product and a handful of resource types, a policy engine evaluating attributes against requests will likely cover most of what you want. You may not need ReBAC, but if you are deploying agents, you need some kind of policy-driven, runtime-evaluated authorization, whatever shape that takes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://auth0.com/fine-grained-authorization" rel="noopener noreferrer"&gt;Auth0's Fine-Grained Authorization&lt;/a&gt;, built on OpenFGA, is one of the more developer-accessible ways to do the ReBAC version of this. The authorization model defines types and relations, and condition checks ask whether a specific relationship holds for a specific request. The following is an example of an OpenFGA authorization model definition with a few relationships between users, agents, and accounts, and a condition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;model&lt;/span&gt;  
  &lt;span class="s"&gt;schema &lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;

&lt;span class="s"&gt;type user&lt;/span&gt;

&lt;span class="s"&gt;type agent&lt;/span&gt;

&lt;span class="s"&gt;type account&lt;/span&gt;  
  &lt;span class="s"&gt;relations&lt;/span&gt;  
    &lt;span class="s"&gt;define owner&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;  
    &lt;span class="na"&gt;define assigned_agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;agent with active_assignment&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;  
    &lt;span class="na"&gt;define can_read&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;assigned_agent or owner&lt;/span&gt;

&lt;span class="na"&gt;condition active_assignment(current_time&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;timestamp, expires_at&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;timestamp) {&lt;/span&gt;  
  &lt;span class="s"&gt;current_time &amp;lt; expires_at&lt;/span&gt;  
&lt;span class="err"&gt;}&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent identity participates in the relationship graph as a first-class type. The condition expression incorporates runtime context — in this case, a time-bound assignment that expires automatically. The check happens at the moment the agent attempts an action, not when the agent was provisioned. If the assignment has expired, the check fails and the agent cannot perform the action.&lt;/p&gt;

&lt;p&gt;Static permissions cannot keep up with agents that make autonomous decisions in real time. By the time a role change has rolled out to fix an over-permissioning issue, the agent has already acted with the old permissions a thousand times. Policy-driven authorization moves the decision to the moment of the action, where the context is known and the authorization layer can evaluate the specific request rather than against a generalization of expected behavior. That is also what makes the audit trail meaningful: every check is a discrete decision tied to a specific request, not a side effect of role membership configured in advance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Observability and Non-Repudiation
&lt;/h2&gt;

&lt;p&gt;A user-proxy or service-account model collapses the chain of accountability. The log shows that "the user" or "the service" did something, with no record of which agent was involved or what task it was executing. With a first-class agent identity, the chain is preserved, and three observability requirements become apparent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Delegation chains must be explicit:&lt;/strong&gt; Each API call should be traceable back through &lt;code&gt;user → agent → sub-agent → tool → resource&lt;/code&gt;, with each hop recorded. OAuth 2.0 Token Exchange supports this, with actor and subject claims expressing the delegation relationship in the token itself.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decisions must be logged, not just actions:&lt;/strong&gt; Knowing the end decision is not enough. You also need to know which plan the agent was executing, which tool calls led to that point, what input shaped the decision, and which policy evaluations approved or denied each step. This is closer to distributed tracing than to traditional audit logging.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-repudiation must extend to original user intent:&lt;/strong&gt; When a user authorizes an action, that authorization should be bound to the specific action approved, not to a generic scope. &lt;a href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-rar" rel="noopener noreferrer"&gt;Rich Authorization Requests (RAR)&lt;/a&gt; support this by letting the authorization request carry structured details about what is being approved. Combined with &lt;a href="https://auth0.com/docs/get-started/authentication-and-authorization-flow/client-initiated-backchannel-authentication-flow" rel="noopener noreferrer"&gt;Client-Initiated Backchannel Authentication (CIBA)&lt;/a&gt; for asynchronous user approval, the audit log can show exactly what the user approved and how the agent executed against it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Centralized token issuance, as with &lt;a href="https://auth0.com/docs/secure/call-apis-on-users-behalf/token-vault" rel="noopener noreferrer"&gt;Auth0's Token Vault&lt;/a&gt;, supports all three requirements by routing every credential through a single control plane. Every token is logged with the agent identity, the user it was issued for, the scope, and the time. Every revocation propagates immediately. The audit infrastructure does not have to reconstruct the chain of custody from scattered logs across multiple tools because the identity provider has already recorded it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building for Agent Identity
&lt;/h2&gt;

&lt;p&gt;Treating AI agents as first-class identities is no longer a theoretical exercise. The traditional models of user-proxy delegation and static service accounts were not designed for non-deterministic actors that interpret natural language at runtime, and the gaps they leave compound quickly as agents take on more of the work the business depends on. The right response is to extend existing identity infrastructure with primitives designed for the way agents behave: distinct identities with their own lifecycles, policies bound to behavior rather than to roles, and audit trails that preserve the delegation chain from user intent through autonomous action.&lt;/p&gt;

&lt;p&gt;Most of the building blocks exist. OAuth 2.0 Token Exchange provides the delegation semantics. CIBA and Rich Authorization Requests provide standards-based approval flows. OpenFGA and similar engines provide the policy infrastructure. Products like Auth0's Token Vault and Fine-Grained Authorization assemble these into developer-accessible platforms, and the patterns documented in OpenFGA's &lt;a href="https://openfga.dev/docs/modeling/agents" rel="noopener noreferrer"&gt;Authorization for Agents&lt;/a&gt; guidance show how to apply them in practice.&lt;/p&gt;

&lt;p&gt;Organizations need to recognize that the identity model is not an implementation detail to be papered over with whichever credential pattern is most familiar to them. The PocketOS incident was not a freak accident. It was the predictable consequence of applying an identity model to a class of actors it was never designed for. AI agents in production deserve the same care that goes into provisioning user accounts and service identities, with the recognition that they are neither.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>security</category>
      <category>agents</category>
    </item>
    <item>
      <title>Why AI Agents Need Their Own Permission Model</title>
      <dc:creator>Cameron Pavey</dc:creator>
      <pubDate>Sat, 10 Oct 2026 09:09:44 +0000</pubDate>
      <link>https://dev.to/auth0/why-ai-agents-need-their-own-permission-model-58i</link>
      <guid>https://dev.to/auth0/why-ai-agents-need-their-own-permission-model-58i</guid>
      <description>&lt;p&gt;AI agents are rapidly making their way into production systems with access to real customer data, internal tooling, and revenue-impacting workflows. A support automation agent might read tickets from a CRM, summarize conversation history, issue partial refunds, create internal escalation tickets, and post a status update to Slack, all within the span of a single task. When these agents work, they can streamline workflows and compress the cost of routine operations. When they fail, they fail differently than traditional automation: non-deterministically, at scale, and often with full production credentials in hand.&lt;/p&gt;

&lt;p&gt;To be useful, an agent needs enough access to execute dynamic, multi-step workflows across multiple tools. But to be safe, it can't operate as a "superuser" with permanent, broad authority over those same tools. Most teams tend to default to access patterns they already know, like long-lived API keys baked into environment variables or OAuth flows designed for human users, and discover quickly that these patterns were never built for non-deterministic software. A misconfigured prompt, a malicious tool response, or a simple hallucination can turn into a significant incident.&lt;/p&gt;

&lt;p&gt;This guide teaches you how to apply the least-privilege principle to autonomous systems. You'll learn how to scope permissions to capabilities rather than resources, issue short-lived credentials tied to specific execution plans, separate identity, authorization, and execution into distinct layers, and introduce human-in-the-loop approval for high-risk actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Access Model Mismatch
&lt;/h2&gt;

&lt;p&gt;Traditional access control models assume you are one of two actors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first is a &lt;strong&gt;human user&lt;/strong&gt;: interactive, bounded by a session, constrained by a UI, and generally predictable within a single workflow.
&lt;/li&gt;
&lt;li&gt;The second is a &lt;strong&gt;backend service&lt;/strong&gt;: deterministic, fixed in its workflow, and auditable through static code paths. AI agents fit neither category.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Agents are non-deterministic by design. Two runs of the same prompt against the same data can produce different tool call sequences. They chain tool invocations in ways the developer did not explicitly write, and they can be influenced by untrusted input through two &lt;a href="https://auth0.com/blog/how-to-fix-five-critical-ai-agent-security-risks/" rel="noopener noreferrer"&gt;well-documented threat classes&lt;/a&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prompt injection:&lt;/strong&gt; Instructions embedded in a document or web page hijack the agent's behavior.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool output poisoning:&lt;/strong&gt; A tool response manipulates subsequent decisions in the chain. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Increasingly, agents also spawn sub-agents, so a delegation chain that starts with a single user request can fan out across many independent contexts before it completes.&lt;/p&gt;

&lt;p&gt;The identity primitives built for these two types of actors (session tokens, service accounts, and &lt;a href="https://auth0.com/docs/get-started/apis/scopes" rel="noopener noreferrer"&gt;OAuth scopes&lt;/a&gt;) are not broken in isolation; used with strict scoping, mediation, and runtime enforcement, they can work well. The issue is that they were not designed for non-deterministic, multi-step autonomous execution, and their &lt;em&gt;typical&lt;/em&gt; usage patterns, which assume a bounded session or a deterministic workflow, do not hold. A session token assumes a bounded interactive flow. A service account assumes deterministic behavior. An OAuth scope like &lt;code&gt;billing:write&lt;/code&gt; assumes an application on the other end interprets it as intended by the user.. When an agent inherits any of these without additional layers, it gets authority without the corresponding accountability structures that made the original model safe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Anti-Patterns
&lt;/h2&gt;

&lt;p&gt;Before reviewing better patterns, it's worth looking at the shortcuts teams typically reach for. Each one feels reasonable in isolation, but together, they can be dangerous.&lt;/p&gt;

&lt;h3&gt;
  
  
  Embedding long-lived credentials
&lt;/h3&gt;

&lt;p&gt;The first common anti-pattern is embedding long-lived API keys directly in agent configurations or environment variables. The agent holds a root credential that grants broad access to a tool, and any compromise of the agent process, logs, or memory exposes that credential. While this is also a problem in non-agentic software, the risk is elevated with agents because, depending on how they are configured, they can be tricked or coerced into taking actions they shouldn’t, or revealing credentials they have access to, much more easily than traditional software with long-lived credentials can be exploited.&lt;/p&gt;

&lt;p&gt;Even with rotation in place via something like Vault or AWS Secrets Manager, the real issue is blast radius; rotation is often inconsistent or slow, and the window between compromise and rotation is more than enough for an attacker (or a misbehaving agent) to do significant damage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Full user OAuth scope inheritance
&lt;/h3&gt;

&lt;p&gt;The second anti-pattern is full user OAuth scope inheritance. A user grants the agent access to their CRM with the permissions they themselves hold, and the agent now has those scopes permanently, across every future action, regardless of what task it is actually performing. The agent inherits authority that the user only ever intended to exercise selectively. However, the agent does not have the discretion, contextual awareness, or accountability that the user has, and it is only a matter of time before the agent accidentally misuses these overly broad permissions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agents with effective superuser privileges
&lt;/h3&gt;

&lt;p&gt;The third, and most damaging anti-pattern, is agents operating with &lt;a href="https://auth0.com/blog/mitigate-excessive-agency-ai-agents/" rel="noopener noreferrer"&gt;effective superuser privileges&lt;/a&gt; over the tools they orchestrate, typically at a system-level, rather than an individual user-level. This happens implicitly when you use the most permissive available scope rather than defining a narrower one. The support agent mentioned earlier might be given a &lt;code&gt;billing:write&lt;/code&gt; scope because it's the only billing scope available, and now it can issue refunds of any size, modify any customer's payment method, and cancel any subscription, even though its actual job is issuing small goodwill refunds within a narrow policy.&lt;/p&gt;

&lt;p&gt;Consider what happens when a support operations agent with &lt;code&gt;billing:write&lt;/code&gt; is asked by a user, "Process a full refund for order #12345." The order total is $10,000 USD. With no way to distinguish between refund sizes it's allowed to process, and those it is not, the agent simply proceeds. Nothing in the access layer prevents it. The damage is done in milliseconds, and the only audit trail is a refund record that looks identical to any legitimate one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capability-Scoped Permissions
&lt;/h2&gt;

&lt;p&gt;The first step in solving the problem of agent permissions is to stop thinking about permissions in terms of resources and start thinking about them in terms of capabilities. A scope like &lt;code&gt;billing:write&lt;/code&gt; describes a resource category and a verb. A capability like &lt;code&gt;billing.refund.issue_under_50_usd&lt;/code&gt; encodes a specific action type, a resource limit, and an implicit risk boundary, although you would not typically encode a capability check into a scope like this, due to the inevitable explosion of scopes that would follow. &lt;/p&gt;

&lt;p&gt;Capability-scoped permissions express authorization in terms the business actually reasons about. When a product manager decides the support agent should be able to issue courtesy refunds up to $50 USD, that decision should live in a declarative policy that the authorization system evaluates, not scattered through imperative checks in the application code that calls the API. Whether the $50 USD threshold is enforced by the authorization engine itself or by an in-code check, such as a dedicated policy layer that is consulted before token issuance is an implementation detail; what matters is that the rule is centrally managed and auditable.&lt;/p&gt;

&lt;p&gt;This is where fine-grained authorization systems come in, and while they solve a specific piece of the puzzle, they're not always an all-in-one solution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://openfga.dev/" rel="noopener noreferrer"&gt;OpenFGA&lt;/a&gt;, an open-source authorization engine created by Auth0's FGA team and now a Cloud Native Computing Foundation (CNCF) project, implements Relationship-Based Access Control (ReBAC). ReBAC excels at modelling &lt;em&gt;who can act on what&lt;/em&gt;. The support agent isn't just a role, but an entity with specific, bounded relationships to specific resource types, and those relationships produce different permissions. You can express rules like "an agent can refund an order only if the order belongs to a customer with whom the agent has an active ticket."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Femxusoohhlea4n817n2l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Femxusoohhlea4n817n2l.png" alt="Diagram showing an authorization flow" width="800" height="1010"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What pure ReBAC does not do is enforce numeric or attribute-based constraints like "refund amount under $100." OpenFGA extends the base ReBAC model with &lt;a href="https://openfga.dev/docs/modeling/conditions" rel="noopener noreferrer"&gt;Conditions&lt;/a&gt;, which let you attach predicates to a relation so that numeric limits, time-bounded grants, and contextual attributes are evaluated as part of the authorization check. &lt;/p&gt;

&lt;p&gt;In OpenFGA, the two layers in the diagram above collapse into a single check: the relationship and its condition are evaluated together, and either can deny. A capability like "agent can refund orders where amount &amp;lt; configured_limit" can be expressed directly in OpenFGA via a condition, with the amount supplied either in the tuple itself or contextually at check time. For teams that prefer to keep pure authorization policy separate from declarative policy code, a dedicated engine like Cedar or OPA layered on top is a legitimate alternative, but it isn't required; OpenFGA can handle both the relationship graph and the attribute constraints.&lt;/p&gt;

&lt;p&gt;Concretely, a support-agent refund capability with a per-agent limit looks like this in OpenFGA's DSL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;model  
  schema 1.1

&lt;span class="nb"&gt;type &lt;/span&gt;user  
&lt;span class="nb"&gt;type &lt;/span&gt;agent  
&lt;span class="nb"&gt;type &lt;/span&gt;customer

&lt;span class="nb"&gt;type &lt;/span&gt;order  
  relations  
    define owner: &lt;span class="o"&gt;[&lt;/span&gt;customer]  
    define can_refund: &lt;span class="o"&gt;[&lt;/span&gt;agent with refund_within_limit]

condition refund_within_limit&lt;span class="o"&gt;(&lt;/span&gt;refund_amount: int, agent_limit: int&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
  refund_amount &amp;lt;&lt;span class="o"&gt;=&lt;/span&gt; agent_limit  
&lt;span class="o"&gt;}&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The capability is granted by writing a relationship tuple that carries the agent's configured limit as &lt;em&gt;persisted condition context&lt;/em&gt;. For a support agent with a $50 USD ceiling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fgaClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;  
  &lt;span class="na"&gt;writes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
    &lt;span class="p"&gt;{&lt;/span&gt;  
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;agent:support_agent_01&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
      &lt;span class="na"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;can_refund&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
      &lt;span class="na"&gt;object&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;order:12345&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
      &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;refund_within_limit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;agent_limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;  
      &lt;span class="p"&gt;},&lt;/span&gt;  
    &lt;span class="p"&gt;},&lt;/span&gt;  
  &lt;span class="p"&gt;],&lt;/span&gt;  
&lt;span class="p"&gt;});&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At check time, the runtime supplies the request-specific half of the condition context (the actual refund amount being attempted):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;allowed&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fgaClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;  
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;agent:support_agent_01&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
  &lt;span class="na"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;can_refund&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
  &lt;span class="na"&gt;object&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;order:12345&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
  &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;refund_amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;75&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;  
&lt;span class="p"&gt;});&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OpenFGA merges the two sides when evaluating the condition, with persisted tuple context taking precedence over request context if a key appears in both. With &lt;code&gt;agent_limit: 50&lt;/code&gt; stored in the tuple and &lt;code&gt;refund_amount: 75&lt;/code&gt; supplied at check time, the condition &lt;code&gt;refund_amount &amp;lt;= agent_limit&lt;/code&gt; evaluates to &lt;code&gt;false&lt;/code&gt; and &lt;code&gt;allowed&lt;/code&gt; comes back &lt;code&gt;false&lt;/code&gt;; the agent cannot autonomously issue a $75 USD refund, and the runtime's next move is to trigger a human approval flow (covered later in this article) rather than request a refund-capable token.&lt;/p&gt;

&lt;p&gt;The practical takeaway is to define capabilities that match actual business boundaries. Not &lt;code&gt;billing:write&lt;/code&gt;, but &lt;code&gt;billing.refund.issue_under_50_usd&lt;/code&gt;. Not &lt;code&gt;crm:read&lt;/code&gt;, but &lt;code&gt;crm.contact.read_for_assigned_tickets&lt;/code&gt;. When you catch yourself defining a capability that feels uncomfortably broad, take a moment to consider if you can strip it back to something less permissive, without compromising the functionality you're building.&lt;/p&gt;

&lt;h2&gt;
  
  
  Task-Scoped Credentials
&lt;/h2&gt;

&lt;p&gt;Capability scoping addresses what the agent can do. Task scoping addresses when and for how long it can do it. These are separate concerns, and conflating them is a common mistake.&lt;/p&gt;

&lt;p&gt;A well-designed agent minimizes or eliminates persistent credentials wherever possible. Instead of holding standing access, it requests short-lived tokens tied to each operation's execution plan. The token carries only the capabilities needed for that plan, expires quickly (minutes, not days), and is discarded after use. In practice, some systems still cache short-lived tokens or rely on scoped service accounts for infrastructure concerns. The goal is less about purity, and more about reducing the window in which any given credential is useful.&lt;/p&gt;

&lt;p&gt;This pattern has several useful properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The window for credential exposure incidents collapses, so a token leaked from an agent's memory is only useful for a short time.
&lt;/li&gt;
&lt;li&gt;The blast radius of a compromised agent shrinks, because the agent can only act with whatever short-lived token it currently holds.
&lt;/li&gt;
&lt;li&gt;The agent itself never touches the root credential. A separate component brokers tokens on the agent's behalf, so even if the agent process is compromised, the underlying OAuth refresh tokens or API keys are not.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Auth0's Token Vault implements this pattern at the identity layer. The vault securely stores OAuth tokens for connected services (Google, Slack, Salesforce, GitHub, and others), and the agent requests a just-in-time access token scoped to a specific task rather than handling the long-lived credential itself. Token Vault implements &lt;a href="https://auth0.com/docs/get-started/authentication-and-authorization-flow/token-exchange-flow" rel="noopener noreferrer"&gt;OAuth 2.0 Token Exchange (RFC 8693)&lt;/a&gt;, giving it a standards-based foundation rather than a proprietary flow. Depending on the flow used, the agent may never touch the underlying refresh token at all; in the &lt;a href="https://auth0.com/blog/developers-guide-flexible-token-exchange-auth0/" rel="noopener noreferrer"&gt;access-token-exchange flow&lt;/a&gt; designed for SPA-plus-backend architectures, the refresh token stays on the Auth0 side entirely.&lt;/p&gt;

&lt;p&gt;For the support agent referenced in this article, this means that when it decides to issue a refund, it does not already hold a refund-capable token. The runtime first evaluates the request against the policy layer, asking questions like: is the amount within the agent's autonomous threshold, is the customer not flagged for fraud review, does the agent have the right relationship to this order, and only then requests a short-lived access token scoped to the refund capability and this specific customer. If the agent is compromised between tasks, there is no standing refund authority to exploit; any earlier token has already expired, and issuing a new one requires passing the policy checks again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identity, Authorization, and Execution Are Three Different Layers
&lt;/h2&gt;

&lt;p&gt;A useful clarification when designing agent access control is to recognize that three distinct concerns are often collapsed into one implementation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Identity&lt;/strong&gt; establishes who the agent is. It answers the question, "which agent, acting on behalf of which user, is making this request?" Identity is relatively stable; an agent typically has a consistent identity across many tasks.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authorization&lt;/strong&gt; determines what an authenticated actor is permitted to do. It answers, "given this identity, what capabilities apply?" Authorization is a policy decision, and it should be evaluated fresh for each operation, not inherited from a long-lived grant.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution enforcement&lt;/strong&gt; determines what is actually allowed in a specific runtime context. It answers, "for this specific call, with this specific payload, does policy permit it?" This includes things like the refund amount, the target resource, and any contextual constraints that cannot be known until the request is actually made.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conflating these leads to familiar failure modes. If identity and authorization are the same thing, then authority is frozen at authentication time and cannot adapt to the task being performed. Similarly, if authorization and execution are coupled, then policy cannot be centrally managed, audited, or updated. Each layer needs its own boundary, and each boundary should be enforced by a different component.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architectural Separation
&lt;/h2&gt;

&lt;p&gt;In practice, handling these three concerns means designing three distinct layers in any production agent system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Identity provider layer
&lt;/h3&gt;

&lt;p&gt;The identity provider layer handles authentication and token issuance. Auth0 authenticates users, manages connected accounts via Token Vault, issues scoped tokens, and handles delegation flows. The identity provider knows who the agent is and what capabilities are available to it, but does not execute business logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Runtime environment layer
&lt;/h3&gt;

&lt;p&gt;The runtime environment layer interprets agent plans, requests credentials from the identity provider, and enforces policy before making calls to downstream tools. This is typically your agent orchestration code, often built on a framework like LangChain, LlamaIndex, or the Vercel AI SDK. &lt;/p&gt;

&lt;p&gt;In a well-designed system, the runtime is separate from the LLM process itself: the LLM produces tool calls, but the runtime decides whether those calls are permitted, requests credentials for them, and makes the actual API calls. Many real-world agent systems fall short of this, passing API keys into tool wrappers or exposing tokens through tool configuration. Keeping credentials out of the LLM's reach is a design goal worth pursuing deliberately, not a property you get for free.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tool layer
&lt;/h3&gt;

&lt;p&gt;The tool layer is the downstream system, such as the CRM, billing service, or Slack API, that actually executes the operation. The tool layer is responsible for its own enforcement and logging, independent of whatever the agent or runtime believes is permitted. This defense ensures that even if the runtime is compromised, the tool still enforces its own authorization rules.&lt;/p&gt;

&lt;p&gt;When this separation is maintained, it has a critical security benefit: the LLM process itself never holds credentials. The runtime brokers them, presents them for individual API calls, and discards them. A prompt injection attack that tries to convince the LLM to leak "its credentials" has nothing to leak, because the LLM was never given any.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh3ydhquhax38fblaost6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh3ydhquhax38fblaost6.png" alt="Diagram showing architecture of a ReBAC enabled authorization flow" width="800" height="1330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Approval Boundaries for High-Risk Actions
&lt;/h2&gt;

&lt;p&gt;Even with capability-scoped, task-scoped credentials, some actions warrant an extra step: &lt;em&gt;explicit human approval at the moment of execution&lt;/em&gt;. This is not a fallback for a broken permission model. It's an intentional design choice for operations whose risk profile justifies the extra friction.&lt;/p&gt;

&lt;p&gt;The question is, what actions cross that line. The support agent issuing a $5 USD refund probably should not require human approval; the automation value comes from handling those without intervention. The same agent attempting a $2,000 USD refund, a refund to an account flagged for fraud review, or any destructive action on a customer record is a different matter entirely. These are the operations where getting it right matters more than getting it fast.&lt;/p&gt;

&lt;p&gt;Auth0 implements asynchronous approval using the &lt;a href="https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html" rel="noopener noreferrer"&gt;Client-Initiated Backchannel Authentication (CIBA) standard&lt;/a&gt;, an OpenID Foundation specification that lets a client (such as an agent backend) request user approval out-of-band from a separate trusted device. Most production agent systems today handle approval through bespoke flows, internal policy engines, or workflow systems like Temporal. CIBA offers a standards-based alternative that slots into existing OAuth infrastructure rather than requiring a parallel approval system. When the support agent identifies that an operation exceeds its autonomous threshold, its backend sends a &lt;a href="https://auth0.com/blog/async-ciba-python-langgraph-auth0/" rel="noopener noreferrer"&gt;CIBA request&lt;/a&gt; to Auth0. The user receives a push notification on their enrolled mobile device via the Auth0 Guardian app (with sms or email also as supported channels), and the agent polls for a response. If the user approves, the agent receives a token scoped to that specific action. If the user denies or the request times out, nothing happens.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhc5djmv30p8yfbkl5wqf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhc5djmv30p8yfbkl5wqf.png" alt="Diagram showing authorization flow" width="800" height="1044"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CIBA becomes more powerful when combined with &lt;a href="https://datatracker.ietf.org/doc/html/rfc9396" rel="noopener noreferrer"&gt;Rich Authorization Requests (RAR)&lt;/a&gt;, an OAuth 2.0 extension that lets the authorization request carry detailed, structured context about what is actually being approved. Rather than a generic "approve access to billing?" prompt, the user sees something specific, such as "approve refund of $2,000 to customer Jane Doe for order #12345." The structured &lt;code&gt;authorization_details&lt;/code&gt; payload makes the approval verifiable and auditable in a way a broad scope grant cannot be.&lt;/p&gt;

&lt;p&gt;This pattern directly addresses the $10,000 refund scenario. The agent's capability scope permits refunds under a low threshold autonomously. For anything above that, the agent cannot proceed without triggering a CIBA flow, and the user sees the exact amount and target before approving. The agent cannot bypass this because the token it needs to execute the refund is only issued after approval.&lt;/p&gt;

&lt;h2&gt;
  
  
  Observability and Control
&lt;/h2&gt;

&lt;p&gt;The final layer is observability. An agent that operates under strict permission controls is still operating autonomously across many systems, and understanding what it did, why, and on whose behalf is essential for debugging, incident response, and compliance.&lt;/p&gt;

&lt;p&gt;Three elements matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Audit trails should capture agent decisions, not just actions. It's not enough to know that a refund was issued; you want to know which plan the agent was executing, which tool calls led to this point, and what context informed the decision.
&lt;/li&gt;
&lt;li&gt;Delegation chains should be explicit, so a given API call is traceable back through &lt;code&gt;user → agent → sub-agent → tool → resource&lt;/code&gt;, with each hop recorded.
&lt;/li&gt;
&lt;li&gt;Revocation has to be fast and precise. If an agent is misbehaving, you need to revoke its standing grants, invalidate in-flight tokens, and halt further tool calls, without taking down the entire system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Auth0's platform provides this infrastructure by centralizing token issuance, which means every token vended to an agent passes through a single control plane. Revoking a connection in &lt;a href="https://auth0.com/blog/auth0-token-vault-secure-token-exchange-for-ai-agents/" rel="noopener noreferrer"&gt;Token Vault&lt;/a&gt; invalidates future token exchanges immediately, and the audit logs reflect every issuance and use.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Permission Model for Agents
&lt;/h2&gt;

&lt;p&gt;AI agents require access control models designed for non-deterministic, autonomous workflows. Permissions inherited wholesale from a human user, or from a static service account, are not fit for purpose. The agent will use them in ways neither the user nor the developer anticipated, and the existing access layer will not notice.&lt;/p&gt;

&lt;p&gt;The three patterns that form the foundation of production-ready agent architectures are: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;capability-scoped permissions that encode specific actions and limits rather than broad resource verbs&lt;/li&gt;
&lt;li&gt;task-scoped credentials that are short-lived and tied to specific execution plans rather than persistent grants&lt;/li&gt;
&lt;li&gt;layered enforcement that separates identity, authorization, and execution into distinct components &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, these patterns contain the blast radius of agent mistakes without removing the autonomy that makes agents useful.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>agents</category>
      <category>security</category>
    </item>
    <item>
      <title>Don't Abandon the Discipline</title>
      <dc:creator>Cameron Pavey</dc:creator>
      <pubDate>Thu, 12 Mar 2026 11:56:32 +0000</pubDate>
      <link>https://dev.to/cpave3/dont-abandon-the-discipline-560k</link>
      <guid>https://dev.to/cpave3/dont-abandon-the-discipline-560k</guid>
      <description>&lt;p&gt;You've adopted agentic coding tools, and you can now output a complete feature in an afternoon. The speed is unlike anything you've experienced before, and it's addictive. You just want to keep building, keep pushing new features out into your customers' hands.&lt;/p&gt;

&lt;p&gt;This is the reality for a lot of us right now. But as adoption of AI coding tools continues, I've noticed a concerning trend. Day by day, teams are abandoning established, known-good practices that have helped us deliver software well for years. Practices like Agile, code reviews, customer validation, and automated testing are being left by the wayside in favour of a "speed at all costs" approach. I've seen sentiments including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Human code reviews are irrelevant for AI code. As long as it works, reviews just slow things down.&lt;/li&gt;
&lt;li&gt;There's no point doing Agile development. The overhead takes away from the speed of AI development.&lt;/li&gt;
&lt;li&gt;Automated testing is unnecessary. If the feature works, why bother?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The common theme is that best practices are being dismissed because they are perceived to slow down the otherwise fast pace of agentic coding. This misses the point entirely. Yes, writing code used to be the bottleneck. AI has compressed or removed it. But writing code was also where a lot of understanding happened. Developers discovered edge cases during implementation, challenged assumptions while debugging, and built mental models line by line. AI didn't just speed up the bottleneck, it moved it. The constraint is now understanding: knowing what to build, knowing that what you built is right, and having the confidence to ship it. None of that got faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agile Was About Validation, Not Velocity
&lt;/h2&gt;

&lt;p&gt;Agile has been the industry's best answer to building quality software for decades. It put a focus on sustainable development, working software, quality practices, and crucially, early iteration and continuous delivery.&lt;/p&gt;

&lt;p&gt;But here's what people forget: Agile was never about building faster. Speed was a side effect of breaking work into smaller chunks and validating as you built. The real value was in compressing learning loops. Frequent contact with reality, so you could course-correct before investing too much in a wrong direction.&lt;/p&gt;

&lt;p&gt;Small sprints, small PRs, daily standups - these were all mechanisms to force feedback. The chunk size was small because &lt;em&gt;building&lt;/em&gt; was slow. Small chunks were the only way to learn fast enough to avoid wasting months on the wrong thing.&lt;/p&gt;

&lt;p&gt;Now, people are applying AI speed to skip the learning, not accelerate it. You can build a feature in an afternoon, but you still don't know if users want it until they touch it. That hasn't changed. The &lt;a href="https://www.faros.ai/blog/key-takeaways-from-the-dora-report-2025" rel="noopener noreferrer"&gt;2025 DORA Report&lt;/a&gt; makes this tangible: AI adoption actually correlates with a 7.2% &lt;em&gt;reduction&lt;/em&gt; in delivery stability. DORA's core finding is that AI is an "amplifier." It magnifies the strengths of high-performing organisations and the dysfunctions of struggling ones in equal measure.&lt;/p&gt;

&lt;p&gt;By regressing from Agile back to waterfall, and then applying the speed gains from AI, you're now building your misconceptions perfectly, efficiently, and potentially irreversibly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Double Abdication
&lt;/h2&gt;

&lt;p&gt;Something special happens when you combine the resurgence of waterfall-like practices with the adoption of AI coding tools. You lose understanding from two directions at once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first abdication is validation.&lt;/strong&gt; Moving from Agile back to waterfall removes continuous validation. You plan, then you build, but you don't &lt;em&gt;understand&lt;/em&gt; what you're building the way you do with iterative delivery. In Agile, you continuously challenge and validate assumptions through iteration. In waterfall, you assume your assumptions are correct and find out whether they were at the end.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The second abdication is authorship.&lt;/strong&gt; AI coding has a subtle but significant side effect: you are no longer writing code, you are reviewing someone else's. Your understanding of the code is fundamentally more superficial than when you write it yourself, think through it as you go, and build a mental model line by line.&lt;/p&gt;

&lt;p&gt;When you combine these, the worst case scenario looks like this: assume how the feature should work, generate it, review it superficially, ship it. You end up with code that no one on your team &lt;em&gt;actually&lt;/em&gt; understands at an ownership level, based on assumptions that nobody properly validated.&lt;/p&gt;

&lt;p&gt;And the code you generate is still your code. It has your name on it. You own the consequences. When the system goes down at 3am on a Saturday and you have to wake up and fix it, there is no one more suitable to delegate to. If you have no clue how it works, you are owner in name only. If you own the planning and know how the feature &lt;em&gt;should&lt;/em&gt; work, you might be able to figure it out at the eleventh hour. But if you double-abdicated and can't explain the feature end to end, let alone the code, you're flying blind.&lt;/p&gt;

&lt;p&gt;The data backs this up. &lt;a href="https://www.helpnetsecurity.com/2025/12/23/coderabbit-ai-assisted-pull-requests-report/" rel="noopener noreferrer"&gt;CodeRabbit's study of 470 pull requests&lt;/a&gt; found that AI-authored code contains 1.7x more issues than human-written code, with 75% more logic errors and 3x more readability issues. And &lt;a href="https://graphite.com/blog/how-i-got-claude-to-write-better-code" rel="noopener noreferrer"&gt;Graphite's data&lt;/a&gt; shows that only 24% of PRs exceeding 1,000 lines of code receive any review comments at all. Reviewers don't carefully evaluate large AI-generated changesets. They rubber-stamp them.&lt;/p&gt;

&lt;p&gt;You've neither designed nor learned. You've orchestrated. Maybe this gets you the outcome you want. But you're trading one competitive edge that was scarce (truly proper Agile validation and deep understanding) for one that everyone can buy for $200 per month (rapid build time). When everyone has access to something, that's not a competitive advantage - that's table stakes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Was Agile a Stepping Stone?
&lt;/h2&gt;

&lt;p&gt;Let's consider the devil's advocate position. Perhaps Agile was the best we could do &lt;em&gt;given slow build times&lt;/em&gt;, but it was never the ideal end state. Maybe waterfall was always conceptually right. Plan comprehensively, build once, ship. If building is now near-instant, doesn't comprehensive upfront planning make sense again?&lt;/p&gt;

&lt;p&gt;No. Because the argument rests on a false promise: that when something is wrong, you can "just regenerate."&lt;/p&gt;

&lt;p&gt;You can't. You have two options, and both are bad. You do a partial regeneration, patching the parts that were wrong while keeping the parts that weren't. This leaves echoes. Maybe it fixes the immediate problem, but it will never be truly clean. Remnants of invalid architecture accumulate as tech debt, and over time your architecture reflects every wrong assumption you ever made, partially corrected but never resolved. Option two, you do a full regeneration. Throw it all away and start again. Assuming &lt;em&gt;some&lt;/em&gt; of it was fine, you've now discarded working code and spun the wheel of chance again, hoping the AI produces something better this time. This isn't engineering. This is gambling.&lt;/p&gt;

&lt;p&gt;As Eisenhower put it, "Plans are worthless, but planning is everything." Understanding doesn't come from the plan itself. It comes from the process of planning and then confronting reality. The plan is a hypothesis. Iteration is how you test it. AI-accelerated waterfall risks skipping the thinking entirely, because doing is so cheap that thinking feels like a waste of time.&lt;/p&gt;

&lt;p&gt;An &lt;a href="https://www.infoq.com/articles/PDCA-AI-code-generation/" rel="noopener noreferrer"&gt;InfoQ experimental study&lt;/a&gt; found that in unstructured AI coding sessions, 80% of tokens were spent &lt;em&gt;after&lt;/em&gt; the agent declared the task complete. All of that effort went into debugging, resolving incomplete implementation, and correcting assumptions. The "build fast, fix later" approach doesn't even save time. The thinking you skip upfront just comes back as rework downstream.&lt;/p&gt;

&lt;h2&gt;
  
  
  Theory, Meet Practice
&lt;/h2&gt;

&lt;p&gt;Even with those problems, you might still feel like the logic holds. If we just plan &lt;em&gt;well enough&lt;/em&gt;, AI-accelerated waterfall should work. It makes intuitive sense.&lt;/p&gt;

&lt;p&gt;But in practice, reality continues to surprise us. We're just surprised faster, and more expensively, because we built more before discovering we were wrong.&lt;/p&gt;

&lt;p&gt;When building is cheap, it &lt;em&gt;feels&lt;/em&gt; wasteful to spend time planning and validating. Why spec it out when you can just build it and see? Why write acceptance criteria when the AI can generate the feature in twenty minutes? Because the cost was never in the building. The cost is in being wrong at scale. Wrong assumptions baked into architecture. Wrong UX shipped to customers. Wrong patterns replicated across the codebase by an AI that doesn't know they're wrong, and will happily propagate them into every file it touches.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.augmentcode.com/guides/ai-coding-agents-for-spec-driven-development-automation" rel="noopener noreferrer"&gt;Augment Code found&lt;/a&gt; that multi-file AI coding tasks succeed at only 19.36% accuracy, while single-function tasks achieve 87.2%. The difference is specification quality. The less you think before generating, the worse the output, and this scales non-linearly with complexity. A vague prompt for a small function might produce something close enough. A vague prompt for a multi-service feature produces something that looks correct, passes a cursory review, and fails in production in ways you never anticipated because you never thought through the edge cases.&lt;/p&gt;

&lt;p&gt;There's a deeper problem here too. When you tell an AI "give me all the users born after a certain date who like cheese," you get working code. But you have no idea how it works. You didn't think about what data structure to use, how to filter efficiently, or what happens when the dataset is large. You skipped all of that, and the code runs, so it feels like it doesn't matter.&lt;/p&gt;

&lt;p&gt;But it does matter, because that thinking is where understanding comes from. When a developer solves a problem by hand, they spend far more time thinking about it than writing code. They need to understand the API they're calling, the concept of the operations they're performing, and they need to sequence the solution in their head. This compounds over time and becomes knowledge. AI lets you skip straight to a working result without building that mental model. You can &lt;em&gt;use&lt;/em&gt; the output without &lt;em&gt;understanding&lt;/em&gt; the output. Which is fine until something breaks, or until you need to make a judgment call the AI can't make for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agile With Larger Brushstrokes
&lt;/h2&gt;

&lt;p&gt;So what actually works? Not abandoning discipline for speed. But not clinging to pre-AI chunk sizes either. The old model of sub-200 line PRs and two-week sprints was optimised for a world where building was slow. That constraint has genuinely changed.&lt;/p&gt;

&lt;p&gt;Think of it this way. In waterfall, you might plan six months of work, build it, and find out at the end whether your assumptions were right. Agile compressed that. You plan a task thay might be sixty minutes of work, build it, validate, and iterate. What AI enables is somewhere in between: you plan six hours of work, generate it, and validate through the stack. The planning horizon grew because AI made implementation faster, but it didn't grow to six months. The feedback loops are still short. You're still validating continuously. The chunks got bigger, but the discipline didn't disappear.&lt;/p&gt;

&lt;p&gt;AI changed the cost of building, but it didn't change the cost of being wrong. So the chunk size can grow, but only if the feedback loops are preserved at the right points. Concretely, this looks like three shifts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Specs as collaborative hypotheses, not contracts.&lt;/strong&gt; Product, design, and engineering converge on a shared specification &lt;em&gt;before&lt;/em&gt; AI generates anything. Not Big Design Up Front. A focused conversation, fifteen to sixty minutes, that surfaces assumptions, defines edge cases, and gives the AI coherent direction. Pre-AI, developers accumulated this understanding incidentally during slow implementation. AI removes that incidental learning, so you need to replace it with something deliberate. The spec is that replacement. It's a hypothesis about what to build and how, not a guarantee that you've thought of everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stacked PRs as iterative delivery.&lt;/strong&gt; AI can generate a large changeset in hours, but it shouldn't land as a single monolithic pull request. Instead, it lands as three to five sequential PRs, each validated independently. Preview deployments per PR mean design and product see real output continuously, not at a staging gate at the end. Each PR in the stack is a learning checkpoint where assumptions meet reality. A monolithic AI-generated PR is waterfall at the code level, while a stack of validated PRs is Agile at the code level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layered review that matches the new reality.&lt;/strong&gt; AI review tools handle the mechanical checks: style, common bugs, security patterns. Humans focus on what AI genuinely can't judge. Does this architecture make sense for where we're headed? Does this match what the customer actually needs? The review bottleneck isn't removed. It's redirected to where human judgment is irreplaceable.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.faros.ai/blog/key-takeaways-from-the-dora-report-2025" rel="noopener noreferrer"&gt;DORA Report&lt;/a&gt; is explicit about this: "working in small batches amplifies AI's positive effects." Teams with strong engineering discipline see dramatically better outcomes from AI adoption. Teams without it see negative ROI, producing technical debt faster than they ever could by hand. The discipline isn't the obstacle. It's the multiplier.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottleneck Moved. Fix It, Don't Remove It.
&lt;/h2&gt;

&lt;p&gt;The teams struggling with AI adoption are the ones treating every source of friction as an obstacle to remove. Code review is slow? Skip it. Sprint planning takes time? Drop it. Writing tests feels redundant when the AI code works? Don't bother.&lt;/p&gt;

&lt;p&gt;The teams succeeding are the ones recognising that the bottleneck has shifted from building to understanding, and investing accordingly.&lt;/p&gt;

&lt;p&gt;Pre-AI, understanding came partly for free through the act of writing code. AI removed that incidental learning, and nothing automatically replaced it. The practices that work in an AI-native world (spec-driven development, stacked validation, layered review) aren't new overhead bolted onto a fast process. They're the deliberate replacement of understanding that used to come for free during slow implementation. Without them, you're building fast but building blind.&lt;/p&gt;

&lt;p&gt;You can have the speed. You can have the quality. But only if you invest in the thinking that the speed demands. The competitive advantage was never building fast. Everyone can do that now. The competitive advantage is understanding what to build, and knowing that what you built is right. That was always the hard part. It still is.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>codequality</category>
      <category>productivity</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Automated Code Review: Benefits, Tools &amp; Implementation (2026 Guide)</title>
      <dc:creator>Cameron Pavey</dc:creator>
      <pubDate>Thu, 05 Mar 2026 11:24:29 +0000</pubDate>
      <link>https://dev.to/cpave3/automated-code-review-benefits-tools-implementation-2026-guide-5dgd</link>
      <guid>https://dev.to/cpave3/automated-code-review-benefits-tools-implementation-2026-guide-5dgd</guid>
      <description>&lt;p&gt;Code review has become the single biggest bottleneck in modern software development. As AI coding tools accelerate generation, with &lt;a href="https://www.elitebrains.com/blog/aI-generated-code-statistics-2025" rel="noopener noreferrer"&gt;41% of all code now AI-assisted&lt;/a&gt;, review queues have ballooned, creating a paradox where individual developer speed rises while organizational throughput stalls or declines. The &lt;a href="https://cloud.google.com/blog/products/devops-sre/announcing-the-2024-dora-report" rel="noopener noreferrer"&gt;DORA 2024 report&lt;/a&gt; found that a 25% increase in AI tool adoption correlated with a 7.2% decrease in delivery stability, largely because AI enables larger changesets that overwhelm review capacity.&lt;/p&gt;

&lt;p&gt;This guide walks you through the three levels of automated code review. From basic linting through Static Analysis to AI-powered semantic analysis, you will see how to implement a system that turns review from a bottleneck into a competitive advantage.&lt;/p&gt;

&lt;p&gt;The stakes are real. Research &lt;a href="https://www.it-cisq.org/cisq-files/pdf/CPSQ-2020-report.pdf" rel="noopener noreferrer"&gt;consistently shows&lt;/a&gt; that a bug caught in production costs 10x more than one found during design, with &lt;a href="https://www.theregister.com/2021/07/22/bugs_expense_bs/" rel="noopener noreferrer"&gt;some estimates&lt;/a&gt; putting that multiplier as high as 100x. The &lt;a href="https://www.it-cisq.org/wp-content/uploads/sites/6/2022/11/CPSQ-Report-Nov-22-2.pdf" rel="noopener noreferrer"&gt;Consortium for IT Software Quality&lt;/a&gt; pegs the total US cost of poor software quality at $2.41 trillion annually. Yet &lt;a href="https://www.linkedin.com/pulse/why-your-pull-requests-stuck-how-unstick-them-daksh-guard-09gyc/" rel="noopener noreferrer"&gt;analysis of 730,000+ pull requests across 26,000 developers&lt;/a&gt; reveals that PRs sit idle for 5 out of every 7 days of cycle time. Automated code review directly attacks this gap by catching defects earlier, accelerating merge velocity, and freeing human reviewers to focus on architecture and business logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AI code explosion has made review the new constraint
&lt;/h2&gt;

&lt;p&gt;A &lt;a href="https://www.faros.ai/blog/ai-software-engineering" rel="noopener noreferrer"&gt;2025 Faros AI study&lt;/a&gt; of 10,000+ developers found that engineers using AI tools complete 21% more tasks and merge 98% more PRs, but PR review time increased by 91%. Teams that once handled 10 to 15 PRs per week now face 50 to 100. Features that take 2 hours to generate can require 4 hours to review. &lt;a href="https://linearb.io/resources/engineering-benchmarks" rel="noopener noreferrer"&gt;LinearB's 2025 benchmark&lt;/a&gt; of 8.1 million PRs confirmed the pattern: AI-generated PRs wait 4.6x longer before a reviewer picks them up.&lt;/p&gt;

&lt;p&gt;More code is entering pipelines than human reviewers can properly validate. A &lt;a href="https://www.coderabbit.ai/blog/state-of-ai-vs-human-code-generation-report" rel="noopener noreferrer"&gt;CodeRabbit analysis&lt;/a&gt; of 470 GitHub PRs found AI-generated code produces 1.7x more issues than human-written code, logic errors up 75%, security vulnerabilities up 1.5 to 2x, and performance inefficiencies appearing 8x more frequently. The &lt;a href="https://www.sonarsource.com/state-of-code-developer-survey-report.pdf" rel="noopener noreferrer"&gt;Sonar 2026 State of Code&lt;/a&gt; survey confirmed that 96% of developers don't fully trust AI-generated code's functional accuracy, yet only 48% always verify it before committing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F389ssjpay2me8urq1ir9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F389ssjpay2me8urq1ir9.png" alt="The cycle of increasing presssure" width="759" height="1660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;DORA's 2024 research identified the root cause: AI tools violate &lt;a href="https://graphite.com/guides/best-practices-managing-pr-size" rel="noopener noreferrer"&gt;small-batch principles&lt;/a&gt; by enabling larger changesets that increase risk. &lt;a href="https://graphite.com/guides/common-developer-kpis-graphite-insights" rel="noopener noreferrer"&gt;Elite-performing teams&lt;/a&gt; deploy multiple times daily with sub-5% change failure rates. However, AI adoption without review automation pushes teams toward larger batches, eroding the very practices that make elite performance possible. The path forward is automating the review process itself, not just code generation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Level 1: linting and formatting eliminate the noise
&lt;/h2&gt;

&lt;p&gt;The foundation of any automated review system is deterministic tooling that enforces consistency and catches syntax-level issues before they reach human reviewers. This layer eliminates style debates entirely and ensures every PR starts from a clean baseline.&lt;/p&gt;

&lt;p&gt;Linters analyse your code for logical errors, anti-patterns, and style violations. Rather than checking whether code runs, they encode your team's standards as rules applied automatically on every change. Formatters handle a narrower but equally important job: they take any valid code and rewrite it into a single canonical style, making diffs cleaner and reviews faster. The two tools work in tandem, with the linter catching what you mean, and the formatter controlling how it looks.&lt;/p&gt;

&lt;p&gt;In the JavaScript ecosystem, &lt;a href="https://eslint.org/" rel="noopener noreferrer"&gt;ESLint&lt;/a&gt; and &lt;a href="https://prettier.io/" rel="noopener noreferrer"&gt;Prettier&lt;/a&gt; are the dominant tools for these roles respectively, and both saw significant releases in early 2026. ESLint's v10 completed a multi-year architectural overhaul, added multithreading for large codebases, and expanded beyond JavaScript to cover CSS, HTML, JSON, and Markdown. Prettier's v3.8 introduced a Rust-powered CLI with meaningful speed improvements. Together they cover virtually every file type in a modern web project.&lt;/p&gt;

&lt;p&gt;Implementing both via GitHub Actions is straightforward and should be the first automation any team deploys:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Code Quality&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;lint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;20'&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx eslint . --cache --max-warnings &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx prettier --check .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In CI, run formatters in &lt;code&gt;--check&lt;/code&gt; mode (developers should fix issues locally) and enforce passing checks via branch protection rules. Adding ESLint caching and parallel jobs per language keeps feedback under 30 seconds, which is critical for developer adoption. Pre-commit hooks using tools like Husky and lint-staged catch issues before they even reach CI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Level 2: SAST and security scanning catch what linters miss
&lt;/h2&gt;

&lt;p&gt;Static Application Security Testing tools analyse code for vulnerabilities, complexity, and deeper quality issues that pattern-based linters cannot detect. &lt;a href="https://www.sonarsource.com/products/sonarqube/whats-new/2026-1/" rel="noopener noreferrer"&gt;SonarQube Server 2026.1 LTA&lt;/a&gt; leads this category with support for 30+ languages, advanced taint analysis tracking data flow across functions and files, and detection of &lt;a href="https://graphite.com/guides/owasp-code-review-guidelines" rel="noopener noreferrer"&gt;OWASP Top 10 vulnerabilities&lt;/a&gt; including SQL injection, XSS, SSRF, command injection, and path traversal. SonarQube's AI CodeFix feature uses LLMs to generate remediation suggestions for detected issues, while its AI Code Assurance capability automatically identifies and applies stricter quality gates to AI-generated code.&lt;/p&gt;

&lt;p&gt;SAST tools commonly detect injection flaws (SQL injection, XSS, command injection, LDAP injection, SSRF, and XXE), data exposure issues (hardcoded secrets and credentials, sensitive data in logs, missing encryption), memory and buffer issues (buffer overflows, use-after-free, integer overflows), and input validation failures (path traversal, insecure deserialization, unvalidated redirects).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fti59ftgtbuoqzue9komn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fti59ftgtbuoqzue9komn.png" alt="The different levels of automated review" width="800" height="1062"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Detection rates vary significantly. On the OWASP Benchmark, modern AI-enhanced SAST tools like &lt;a href="https://qwiet.ai/beating-the-owasp-benchmark/" rel="noopener noreferrer"&gt;Qwiet AI have achieved&lt;/a&gt; 100% true positive rates with 25% false positive rates, while traditional tools historically scored around 33%. SonarQube achieves false positive rates as low as 1% on mature codebases. The key advance in 2025 to 2026 has been combining SAST with LLM-based post-processing. &lt;a href="https://aisecurity-portal.org/en/literature-database/llm-driven-sast-genius-a-hybrid-static-analysis-framework-for-comprehensive-and-actionable-security/" rel="noopener noreferrer"&gt;One study showed this combination reduced false positives by 91%&lt;/a&gt; compared to standalone Semgrep scanning.&lt;/p&gt;

&lt;p&gt;SonarQube's &lt;a href="https://docs.sonarsource.com/sonarqube-server/10.3/user-guide/clean-as-you-code/" rel="noopener noreferrer"&gt;Clean as You Code&lt;/a&gt; philosophy, where quality gates apply only to new code rather than the entire codebase, makes adoption practical for legacy projects. Configure gates to fail on any new blocker or critical vulnerability, while incrementally addressing existing technical debt. This approach follows a zero-noise principle: only flag issues developers can act on right now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Level 3: AI-powered review and workflow platforms change everything
&lt;/h2&gt;

&lt;p&gt;The most significant shift in 2025 to 2026 has been the emergence of &lt;a href="https://graphite.com/guides/ai-code-reviewers-how-they-work" rel="noopener noreferrer"&gt;AI-powered code review&lt;/a&gt; that understands code semantics, developer intent, and project context, moving well beyond pattern matching into genuine comprehension. This is where platforms like &lt;strong&gt;Graphite&lt;/strong&gt; operate, combining AI review intelligence with workflow automation to address the full "outer loop" of development.&lt;/p&gt;

&lt;p&gt;The AI foundation is now proven. Anthropic's Claude model family powers multiple code review tools across the Claude Sonnet, Haiku, and Opus tiers, balancing capability, speed, and cost for different review workloads. Claude Code includes a built-in &lt;code&gt;/code-review&lt;/code&gt; command that launches four parallel review agents, scores issues by confidence, and surfaces only findings above an 80% confidence threshold — important for &lt;a href="https://graphite.com/guides/ai-code-review-false-positives" rel="noopener noreferrer"&gt;managing false positives&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://graphite.com/" rel="noopener noreferrer"&gt;Graphite&lt;/a&gt; exemplifies the Level 3 platform approach. Following its acquisition by Cursor in December 2025 (at a valuation exceeding its previous $290M), Graphite serves 100,000+ developers across 500+ companies including &lt;a href="https://graphite.com/customer/shopify" rel="noopener noreferrer"&gt;Shopify&lt;/a&gt;, Snowflake, Figma, and Notion. Its thesis: AI tools have dramatically accelerated the "inner loop" of writing code, making the "outer loop" of review, merge, and deploy the new constraint. Graphite addresses this with four integrated capabilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://graphite.com/features/agent" rel="noopener noreferrer"&gt;Graphite Agent&lt;/a&gt;&lt;/strong&gt; provides &lt;a href="https://graphite.com/features/ai-reviews" rel="noopener noreferrer"&gt;AI-powered PR review&lt;/a&gt; built on Anthropic's Claude. Unlike general-purpose AI reviewers with a 5-15% false positive rate, it achieves a &lt;a href="https://www.graphite.com/guides/ai-code-review-false-positives" rel="noopener noreferrer"&gt;5-8%&lt;/a&gt; false positive rate through multi-step validation including voting, chain-of-reasoning, and self-critique. The results are compelling: &lt;a href="https://graphite.com/guides/best-ai-pull-request-reviewers-2025" rel="noopener noreferrer"&gt;67% of AI suggestions lead to actual code changes&lt;/a&gt;, and the tool maintains a 96% positive feedback rate from developers. You can &lt;a href="https://graphite.com/guides/ai-code-review-implementation-best-practices" rel="noopener noreferrer"&gt;define custom review rules&lt;/a&gt; in plain language, something like "ensure auth-service never makes direct database calls", and Graphite Agent enforces them on every PR.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://graphite.com/guides/benefits-of-stacked-diffs-in-code-review" rel="noopener noreferrer"&gt;Stacked PRs&lt;/a&gt;&lt;/strong&gt; directly address the batch-size problem identified by DORA. &lt;a href="https://www.propelcode.ai/blog/pr-size-impact-code-review-quality-data-study" rel="noopener noreferrer"&gt;Analysis of 50,000+ PRs&lt;/a&gt; shows defect detection rates drop from 87% for PRs under 100 lines to just 28% for PRs over 1,000 lines. Stacking breaks large features into small, dependent PRs that build on each other. &lt;a href="https://graphite.com/docs/graphite-cli" rel="noopener noreferrer"&gt;Graphite's CLI&lt;/a&gt; (&lt;code&gt;gt stack submit&lt;/code&gt;) manages the entire stack lifecycle including automatic recursive rebasing. The impact is measurable: &lt;a href="https://graphite.com/customer/semgrep" rel="noopener noreferrer"&gt;Semgrep&lt;/a&gt; saw a 65% increase in code shipped per engineer after adopting stacking, while &lt;a href="https://graphite.com/customer/shopify" rel="noopener noreferrer"&gt;Shopify&lt;/a&gt; reports 33% more PRs shipped per developer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://graphite.com/features/merge-queue" rel="noopener noreferrer"&gt;Merge Queue&lt;/a&gt;&lt;/strong&gt; is the only stack-aware merge queue available, processing dependent PRs in parallel while ensuring the main branch stays green. It supports batching multiple PRs to &lt;a href="https://graphite.com/use-cases/reducing-ci-costs" rel="noopener noreferrer"&gt;reduce CI costs&lt;/a&gt; and hot-fix prioritization for critical changes.&lt;/p&gt;

&lt;p&gt;Customer metrics demonstrate the platform effect. &lt;a href="https://graphite.com/customer/ramp" rel="noopener noreferrer"&gt;Ramp&lt;/a&gt; achieved a 74% decrease in median time between merged PRs (from 10 hours to 3). &lt;a href="https://graphite.com/customer/asana" rel="noopener noreferrer"&gt;Asana&lt;/a&gt; engineers shipped 21% more code and saved 7 hours per week per engineer within 30 days. Across all customers, the average Graphite user merges 26% more PRs while reducing median PR size by 8 to 11%.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rolling out automation without overwhelming your team
&lt;/h2&gt;

&lt;p&gt;The most common failure mode is deploying too many blocking checks at once, triggering alert fatigue that erodes developer trust. Research shows false positives are the &lt;a href="https://graphite.com/guides/benefits-of-automated-code-review-tools" rel="noopener noreferrer"&gt;number-one adoption killer&lt;/a&gt; for automated review tools. The solution is a progressive, trust-building rollout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 1 (Weeks 1 to 4): Foundation.&lt;/strong&gt; Deploy ESLint and Prettier as non-blocking CI checks. Add PR size warnings for changes exceeding 400 lines. Establish baseline metrics: current cycle time, defect escape rate, and PR merge frequency. This phase should be completely frictionless — developers see suggestions but are never blocked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 2 (Weeks 5 to 10): Security gates.&lt;/strong&gt; Introduce SonarQube or equivalent SAST scanning in advisory mode. Configure severity thresholds so only critical security findings (SQL injection, hardcoded secrets) become blocking. All other findings appear as PR comments. Begin tracking false positive rates and tune rules aggressively — a finding that never gets fixed is noise, not signal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 3 (Weeks 11 to 16): AI-powered review.&lt;/strong&gt; Enable &lt;a href="https://graphite.com/features/agent" rel="noopener noreferrer"&gt;Graphite Agent&lt;/a&gt; or equivalent AI review as a non-blocking reviewer. Start with 1 to 3 volunteer teams who provide feedback on suggestion quality. Use this phase to configure custom team rules and &lt;a href="https://graphite.com/guides/calibrate-ai-code-review-feedback" rel="noopener noreferrer"&gt;calibrate the AI&lt;/a&gt; to your codebase's conventions. The key metric to track is acceptance rate — the percentage of AI comments that result in code changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 4 (Week 17+): Full platform.&lt;/strong&gt; Introduce &lt;a href="https://graphite.com/guides/stacked-diffs" rel="noopener noreferrer"&gt;stacked PR workflows&lt;/a&gt;, &lt;a href="https://graphite.com/docs/graphite-merge-queue" rel="noopener noreferrer"&gt;merge queue automation&lt;/a&gt;, and promote AI review to soft-gate status (require acknowledgment of critical findings). Implement &lt;a href="https://graphite.com/docs/insights" rel="noopener noreferrer"&gt;productivity insights&lt;/a&gt; to measure before/after impact.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5r4ggrz8mzbrczvwh40f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5r4ggrz8mzbrczvwh40f.png" alt="The stages of AI code review adoption" width="552" height="1724"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Three principles govern successful rollouts. First, start non-blocking and graduate to blocking only after false positive rates stabilize below 5%. Second, integrate into existing workflows. Review feedback should appear as inline PR comments, not in separate dashboards. Third, measure and share wins: when developers see that automated review caught a real bug or saved them 30 minutes, adoption becomes self-reinforcing. &lt;/p&gt;

&lt;h2&gt;
  
  
  The cost equation favors aggressive automation
&lt;/h2&gt;

&lt;p&gt;The financial case for automated code review is straightforward to model. A team processing 200 PRs monthly that saves 20 minutes of reviewer time per PR at an $80 loaded rate generates roughly $25,600 in annual savings from review efficiency alone. Blocking even 10 high-severity bugs per quarter that would have cost $5,000 each in production adds another $200,000 in avoided remediation costs. Against typical platform costs of $20,000 to $40,000 annually for a 25-person team, the total benefit of roughly $226,000 delivers an ROI of between 5:1 and 11:1 in the first year, depending on platform tier.&lt;/p&gt;

&lt;p&gt;The deeper value is strategic, though. DORA research consistently shows that elite teams combine fast delivery with high stability, and they achieve this through small batches, automated testing, and rapid feedback loops. Automated code review is the mechanism that makes this possible at scale, especially as AI-generated code volumes continue to grow. Teams that treat review as an afterthought will face compounding technical debt: &lt;a href="https://byteiota.com/ai-tech-debt-crisis-75-hit-by-2026-studies-warn/" rel="noopener noreferrer"&gt;75% of technology decision-makers&lt;/a&gt; are projected to face moderate-to-severe technical debt from AI-speed practices by end of 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The automated code review landscape in 2026 has matured into a clear three-level stack. &lt;/p&gt;

&lt;p&gt;Level 1: Linting with ESLint and Prettier. This is table stakes that every team should have deployed. &lt;br&gt;
Level 2: SAST with tools like SonarQube. This catches security vulnerabilities and code smells that linters miss. &lt;br&gt;
Level 3: &lt;a href="https://graphite.com/guides/ai-code-review-agents" rel="noopener noreferrer"&gt;AI-powered semantic review&lt;/a&gt; combined with workflow automation. This represents the frontier, and it's where the highest-impact gains live.&lt;/p&gt;

&lt;p&gt;Platforms like &lt;a href="https://graphite.com/" rel="noopener noreferrer"&gt;Graphite&lt;/a&gt; that integrate AI review, stacked PRs, and merge automation into a unified system address the full outer-loop bottleneck rather than just one piece of it. The data is clear: small PRs reviewed by AI catch 3x more defects than large PRs reviewed by humans alone, and teams using integrated automation platforms ship 20 to 65% more code while maintaining or improving quality. For engineering leaders, the question is no longer whether to automate code review, but how quickly you can reach Level 3.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>softwareengineering</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Understanding Types; Static vs Dynamic, &amp; Strong vs Weak.</title>
      <dc:creator>Cameron Pavey</dc:creator>
      <pubDate>Thu, 10 Dec 2020 11:27:18 +0000</pubDate>
      <link>https://dev.to/cpave3/understanding-types-static-vs-dynamic-strong-vs-weak-4eh4</link>
      <guid>https://dev.to/cpave3/understanding-types-static-vs-dynamic-strong-vs-weak-4eh4</guid>
      <description>&lt;p&gt;Generally speaking, when talking about programming languages, most — if not all — languages can be classified by where they fall in a quadrant, with one axis containing Static and Dynamic typing, and the other containing Strong and Weak typing. But what do these terms mean?&lt;/p&gt;

&lt;h2&gt;
  
  
  Static Typing
&lt;/h2&gt;

&lt;p&gt;Essentially, static typing means that variable types are checked at “compile-time”, or before the code is executed.&lt;/p&gt;

&lt;p&gt;Let’s look at an example in TypeScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function foo(a: number) {
    if (typeof(a) === 'number') {
        return 'number';
    } else {
        return 'not number';
    }
}

foo(1);
foo('1'); // this will throw a compiler error, because it is not a number
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attempting to pass a string into the function &lt;code&gt;foo&lt;/code&gt; after explicitly stating it accepts numbers, would cause it to throw an error such as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Argument of type '"1"' is not assignable to parameter of type 'number'.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Dynamic Typing
&lt;/h2&gt;

&lt;p&gt;On the other hand, dynamic typing means that the variables’ types are checked on the fly, as the code is executed. Consider the following PHP code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function foo($a) {
    if (gettype($a) === 'integer') {
        return 'integer';
    } else {
        // This will never be evaluated
        return 'not integer';
    }
}

echo foo(1); // integer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on what value is passed into the function &lt;code&gt;foo&lt;/code&gt;, the variable &lt;code&gt;$a&lt;/code&gt; could technically be any type, and it is only known which one when the code is executed.&lt;/p&gt;

&lt;p&gt;On the other axis of our Types quadrant, we have Strong and Weak typing. This is a bit more confusing because there is no universal consensus on what these terms mean, even though they get thrown around a lot. That being said, let’s try to understand them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strongly Typed
&lt;/h2&gt;

&lt;p&gt;A lot of — but not all — developers agree that the essence of a strongly typed language is the fact that it converts a variable or value’s type to suit the current situation automatically. This means the &lt;code&gt;"123"&lt;/code&gt; is always treated as a string and is never used as a number without manual conversion or intervention. Consider the following Python code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;z = x + y; // This will fail
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This fails and returns the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Traceback (most recent call last):
  File "main.py", line 3, in &amp;lt;module&amp;gt;
    z = x + y;
TypeError: unsupported operand type(s) for +: 'int' and 'str'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is because the two variables which we tried to add together and assign to &lt;code&gt;z&lt;/code&gt; were different types, and as the error says, adding an int and a string is not supported.&lt;/p&gt;

&lt;h2&gt;
  
  
  Weakly Typed
&lt;/h2&gt;

&lt;p&gt;A weakly typed language is — as you might expect — the opposite of what is described above. The interpreter or compiler attempts to make the best of what it is given by using variables in ways that might seem confusing at first, but make sense once we understand what they are doing.&lt;/p&gt;

&lt;p&gt;This means that in some situations — for example — an integer might be treated as if it were a string to suit the context of the situation.&lt;/p&gt;

&lt;p&gt;To demonstrate, let’s try the above example again in a weakly typed language, like JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const z = x + y; // 12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that this returned 12, instead of 3. This is because rather than performing a math operation as we requested, it treated both values as strings, and concatenated them together, resulting in the string “12”.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Hopefully, this sheds some light on the minutia of language typing systems for those who do not already know. This is just a fundamental summary, and there is, of course, a lot more to it, but this should hopefully be a good starting point in understanding the finer details of your favourite programming languages. If you found this article helpful, please &lt;a href="https://twitter.com/cpave3"&gt;&lt;strong&gt;follow me on Twitter&lt;/strong&gt;&lt;/a&gt; for more content like this!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>100daysofcode</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>How I doubled my productivity while working from home</title>
      <dc:creator>Cameron Pavey</dc:creator>
      <pubDate>Sat, 05 Dec 2020 23:38:11 +0000</pubDate>
      <link>https://dev.to/cpave3/how-i-doubled-my-productivity-while-working-from-home-3537</link>
      <guid>https://dev.to/cpave3/how-i-doubled-my-productivity-while-working-from-home-3537</guid>
      <description>&lt;p&gt;This year has been an interesting one, not least of all because many people around the world - especially in the tech sector - ended up working from home for an extended period of time. &lt;/p&gt;

&lt;p&gt;Working from home used to have something of a workplace stigma around it. Employers were seemingly sceptical of how you could be productive in such a distracting environment. Needless to say, it's been proven it can be done, but what I wasn't expecting is how it would be the catalyst for an actual &lt;em&gt;increase&lt;/em&gt; in productivity. Let's take a look at some of the factors which contributed to this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Track your productivity
&lt;/h2&gt;

&lt;p&gt;The first step on any great transformative journey is to gather data. Gather data of where you started from, what you do each step of the way, and ultimately where you end up. With something as subjective as productivity, this is hard to do. For me, I've had good results using  &lt;a href="https://www.rescuetime.com" rel="noopener noreferrer"&gt;RescueTime&lt;/a&gt;, a tool which shows me a summary of where I spent my time on any given day.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1607207285187%2FaqiKB7RpL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1607207285187%2FaqiKB7RpL.png" alt="Screen Shot 2020-12-06 at 9.27.24 am.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As they say, hindsight is 20/20. Having this kind of information available makes it easy to reflect and see where you wasted time, and where you can win some extra productivity. &lt;/p&gt;

&lt;p&gt;(Whenever I see my stats, seeing how long I spend on Slack per day is a bit jarring at first, but then I remember that on average, office workers spend about 28% of their day on email.)&lt;/p&gt;

&lt;p&gt;Tracking where your time is spent isn't a step you should skip. It's like training your body. If you are trying to lose 10kg but never set foot on the scales, how will you know if it is working?&lt;/p&gt;

&lt;p&gt;If you are trying to boost your productivity, but have no way of measuring it, then good luck to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoid multitasking
&lt;/h2&gt;

&lt;p&gt;Humans are good at a lot of things, but generally not multi-tasking, or remembering lots of small things. This leads to a well-known problem called "context switching", where your productivity will effectively plummet when you try to switch between multiple tasks.&lt;/p&gt;

&lt;p&gt;This happens for a few reasons, but essentially, the "context" that you create around a task consists of lots of small pieces of information which you need to keep in your head to complete the task. If at any point, someone or something interrupts your tasks, all of these small bits of context are at risk of being lost. Even if the distraction is only a minute long, the cost of switching between tasks will make the impact much greater than just 1 minute.&lt;/p&gt;

&lt;p&gt;According to  &lt;a href="https://insights.sei.cmu.edu/devops/2015/03/addressing-the-detrimental-effects-of-context-switching-with-devops.html" rel="noopener noreferrer"&gt;Todd Waits&lt;/a&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[...] Switching between projects requires an operational overhead for the team member to figure out where he or she left off, what needs to be done, how that work fits in the project, etc. Once a team member is assigned five projects, his or her ability to contribute to any given project drops below 10 percent, with 80 percent effort being lost to switching between project contexts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The effects of context switching are well known and impossible to conquer. No matter how good you think you are when it comes to multi-tasking, chances are, you are giving yourself far too much credit. The only way to properly combat the effects of context switching is to remove it from the equation as much as possible.&lt;/p&gt;

&lt;p&gt;Carl Newport's  &lt;a href="https://www.amazon.com.au/Deep-Work-Focused-Success-Distracted/dp/0349411905/" rel="noopener noreferrer"&gt;excellent book "Deep work"&lt;/a&gt; takes a deep dive into this topic and expands on systems which you can use to get the most out of the tasks that matter. If you're going to take anything away from this article, let it be this. &lt;/p&gt;

&lt;p&gt;Don't multitask. It doesn't work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Externalise everything
&lt;/h2&gt;

&lt;p&gt;This was the big one for me.&lt;/p&gt;

&lt;p&gt;It's easy to say "Don't multitask" I know. In practice, it is a lot harder, because even if you don't plan to do it, all it takes is an errant Slack message and then Boom, context switched. (It's worth noting, RescueTime has an awesome Slack integration to mute notifications during focus time. Killer feature)&lt;/p&gt;

&lt;p&gt;How, then, should we solve this? After listening to  &lt;a href="https://alifeofproductivity.com/the-productivity-project/" rel="noopener noreferrer"&gt;Chris Bailey's "The Productivity Project"&lt;/a&gt;, where he touches on the benefits of externalising your todo list, I became a little bit obsessed with lists. At first, it was exactly how Chris suggested it. If you have something you need to do, write it down, so you don't have to think about it anymore, essentially. &lt;/p&gt;

&lt;p&gt;Eventually, this evolved into something of a rolling "work journal". I tried a few tools and ultimately settled on  &lt;a href="https://www.notion.so" rel="noopener noreferrer"&gt;Notion&lt;/a&gt; (but you could use anything really). &lt;/p&gt;

&lt;p&gt;The idea is simple. We &lt;em&gt;know&lt;/em&gt; we will be interrupted, and we &lt;em&gt;know&lt;/em&gt; that when we are, it will tank our productivity because we will lose all the little bits of context in our head.&lt;/p&gt;

&lt;p&gt;What if we could externalise that context though?&lt;/p&gt;

&lt;p&gt;When working through complex issues with a lot of moving parts, any time there was a new noteworthy development, such as a discovery, problem, or solution, I would make a note of it. It doesn't need to be too detailed; it is just a note to your future self so that when you are inevitably distracted, your notes should contain all the information you need to pick up right where you left off.&lt;/p&gt;

&lt;p&gt;This information could be your current challenges you are trying to solve, todo items of what you still need to complete, or even just small anecdotal quirks about the system you are working with, that you don't want to dedicated mental space to remember, but can't afford to forget.&lt;/p&gt;

&lt;p&gt;It doesn't sound like it should work half as well as it does.&lt;/p&gt;

&lt;p&gt;Employing this system, I've been able to largely mitigate a lot of the burden I've typically suffered when it comes to context switching. I don't have to worry about taking a lunch break and losing my place. I don't have to dread every incoming slack message, and I am confident that when we return to the office in force, I won't have to worry as much about that casual tap on the shoulder causing my delicate mental house of cards to come crashing down around me.&lt;/p&gt;

&lt;p&gt;If this has given you some ideas to boost your productivity, or if you have some tricks of your own, I'd love to hear from you. Leave a comment below, or &lt;a href="https://twitter.com/cpave3" rel="noopener noreferrer"&gt;follow me on Twitter&lt;/a&gt; for more content like this.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>programming</category>
      <category>webdev</category>
      <category>100daysofcode</category>
    </item>
    <item>
      <title>Get Developer Super Powers with Vim</title>
      <dc:creator>Cameron Pavey</dc:creator>
      <pubDate>Sun, 18 Oct 2020 22:15:26 +0000</pubDate>
      <link>https://dev.to/cpave3/get-developer-super-powers-with-vim-5f6p</link>
      <guid>https://dev.to/cpave3/get-developer-super-powers-with-vim-5f6p</guid>
      <description>&lt;p&gt;Get Developer Super Powers with&amp;nbsp;Vim&lt;br&gt;
You've probably heard of Vim if you've been working with code for a while. Vim is a command-line text editor available for a lot of operating systems. Initially released in 1991, Vim is one of the most popular text editors in the computing community.&lt;/p&gt;

&lt;p&gt;So why should you care about this old text editor? You should care because it is pretty much ubiquitous, and is worth learning unless your heart already belongs to an alternative like emacs or even nano (if you really want to).&lt;br&gt;
Vim is scary at first. It does so much, and its shortcuts are not likely ones that you have used before. Even quitting out of the software is notoriously tricky for newcomers. Once you have learnt the basics though, they will be the gift that keeps on giving, and I'll tell you why.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ubiquity
&lt;/h2&gt;

&lt;p&gt;Vim is everywhere, even when it isn't. For instance, pretty much any Linux or Mac operating system you touch will have Vim installed and ready to go. With the advent of "Windows Subsystem for Linux" on Windows 10, Vim is now readily available on Windows as well (and could be downloaded anyway before this).&lt;br&gt;
Most code editors also have Vim emulation plugins available, so even if you don't want to use Vim directly, you can still get all of the benefits it brings from the comfort of the code editor or IDE you know and love.&lt;/p&gt;

&lt;h2&gt;
  
  
  Portability
&lt;/h2&gt;

&lt;p&gt;With Vim, you learn the shortcuts and commands once and take them with you everywhere. As mentioned above, any code editor you use will have Vim bindings, and many coding websites like Codewars also support Vim mode. What this means is that all of your devices, editors, and environments can share the same keybindings easily. You don't have to manage the mental burden of memorising different shortcuts for different setups.&lt;/p&gt;

&lt;h2&gt;
  
  
  Productivity
&lt;/h2&gt;

&lt;p&gt;The real secret sauce that makes learning Vim worthwhile is the productivity benefits it offers. A key principle of Vim is to deliver maximum productivity and functionality from the keyboard, without needing to move your hand to the mouse, and without needing to leave the home row so much on the keyboard. This is the main factor that leads to some of the weird shortcuts and keybindings, like :w to save instead of the more commonly used +s.&lt;/p&gt;

&lt;p&gt;The main factor which drove me to become familiar with Vim was the portability so that I would be more productive while working in remote ssh environments. Once I started incorporating it into my daily workflow through the JetBrains IdeaVim plugin, and the corresponding VS Code plugin, I realised that the shortcuts and keybindings themselves were actually a huge time saver even when not working with remote systems. I quickly found myself using v and hjkl to select chunks of text instead of lifting my hand to use the mouse. I often use vim to save, search, replace, copy, paste, and delete text, even though VS Code and PHP Storm have all these features natively. The reason is simple. It's just faster and more natural to do it all in Vim.&lt;/p&gt;

&lt;p&gt;Learning Vim is daunting at first, but give it some time, it will almost certainly be an investment which pays itself off in spades and serves you well for years to come.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>codenewbie</category>
    </item>
  </channel>
</rss>
