<?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: Chris Noring</title>
    <description>The latest articles on DEV Community by Chris Noring (@softchris).</description>
    <link>https://dev.to/softchris</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%2F138723%2F76116967-2b0e-456c-8430-85493e2174aa.jpeg</url>
      <title>DEV Community: Chris Noring</title>
      <link>https://dev.to/softchris</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/softchris"/>
    <language>en</language>
    <item>
      <title>Understanding GitHub Billing and management: from licenses to fair AI credit controls</title>
      <dc:creator>Chris Noring</dc:creator>
      <pubDate>Wed, 12 Aug 2026 13:40:44 +0000</pubDate>
      <link>https://dev.to/azure/understanding-github-billing-and-management-from-licenses-to-fair-ai-credit-controls-1hli</link>
      <guid>https://dev.to/azure/understanding-github-billing-and-management-from-licenses-to-fair-ai-credit-controls-1hli</guid>
      <description>&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/kI8t2ur_Zdc"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Buying GitHub Copilot licenses is only the beginning of the governance story. The licenses are purchased centrally, but administrators still need to decide who receives a seat, how usage is attributed to the right part of the business, and what happens when included AI credits run out.&lt;/p&gt;

&lt;p&gt;Those decisions happen through several related controls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Copilot seat assignment&lt;/strong&gt; determines which people are licensed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost centers&lt;/strong&gt; group attributable usage around a team or business owner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI credit included usage caps&lt;/strong&gt; create boundaries around included credits associated with a cost center's licenses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost-center budgets&lt;/strong&gt; govern paid usage after included credits are exhausted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User-level budgets (ULBs)&lt;/strong&gt; limit how much an individual can consume.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why does this separation matter? Without it, an administrator can easily mistake one control for another. An included usage cap does not set an overage policy, and a cost-center budget does not guarantee every person an equal share. Each control answers a different question.&lt;/p&gt;

&lt;p&gt;This article follows the complete flow, starting before a cost center exists and ending with different policies for Business and Developers, where we place them each in separate cost centers with distinct included-usage boundaries, paid-usage budgets, and ULBs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Series
&lt;/h2&gt;

&lt;p&gt;This is part of a series on GitHub Admin and billing controls.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/azure/turning-surprise-ai-bills-into-accountable-growth-4343"&gt;Managing surprise bills&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/azure/github-admin-ui-billing-api-better-together-for-smarter-spend-decisions-3lp5"&gt;Better together: GitHub Admin UI + Billing API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Understanding GitHub Billing and management: from licenses to fair AI credit controls, &lt;strong&gt;we're here&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Problem 1: A central purchase does not identify who is licensed
&lt;/h2&gt;

&lt;p&gt;Our story starts with a purchase, but purchasing seats does not yet tell us who can use Copilot. This is the first problem to solve because cost centers, budgets, and AI credit controls all depend on GitHub knowing which named users actually hold eligible licenses.&lt;/p&gt;

&lt;p&gt;Suppose an enterprise purchases 400 Copilot seats for a workforce that includes 600 employees. The purchase creates a centrally managed pool of seats. It does not automatically license 400 unspecified people, nor does every developer receive a fraction of a license.&lt;/p&gt;

&lt;p&gt;At this point, the enterprise knows how many seats it owns, but it cannot yet connect those seats to people, teams, or cost centers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution: Assign seats to named users
&lt;/h3&gt;

&lt;p&gt;An administrator assigns those seats to specific users, either directly or through the supported administrative assignment process. At that point, GitHub can distinguish between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A person who belongs to the enterprise but has no Copilot seat.&lt;/li&gt;
&lt;li&gt;A person who has been assigned an eligible Copilot seat.&lt;/li&gt;
&lt;li&gt;A licensed person whose usage is attributable to a particular cost center.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This distinction matters because cost-center included credits are based on attributable eligible licenses, not raw headcount.&lt;/p&gt;

&lt;p&gt;For example, imagine a Developers cost center containing 200 people:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Developers in the cost center&lt;/th&gt;
&lt;th&gt;Developers with eligible Copilot seats&lt;/th&gt;
&lt;th&gt;Licenses that can contribute to the calculation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;120&lt;/td&gt;
&lt;td&gt;120&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The cost center does not receive an included-credit boundary based simply on having 200 members. GitHub looks at the eligible licenses attributable to those members and calculates the included amount from those licenses.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; The exact included-credit amount is calculated by GitHub according to the applicable licenses and product terms. Administrators do not manually divide the enterprise's included credits by cost-center headcount.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now the enterprise knows who is licensed. That solves entitlement, but it creates the next question: when those users consume AI credits, which part of the business owns that usage?&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%2Fqce8d9q7jm2dfkmi1in9.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%2Fqce8d9q7jm2dfkmi1in9.png" alt="A developer cost center" width="799" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 1: A developer cost center&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 2: Licensed usage has no business owner
&lt;/h2&gt;

&lt;p&gt;A list of licensed users is not yet a governance model. Finance and administrators still need to connect usage to the team, program, or financial owner responsible for it.&lt;/p&gt;

&lt;p&gt;Why does this matter? A single enterprise can contain groups with very different usage patterns. Business Operations may have predictable demand, while Developers may run more intensive AI workflows. Treating both groups as one undifferentiated population makes it difficult to protect included usage or govern overage appropriately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution: Use cost centers to establish ownership
&lt;/h3&gt;

&lt;p&gt;Cost centers provide that attribution boundary around resources such as users, teams, or organizations. They do not purchase licenses or assign Copilot seats. Instead, they connect licensed activity to the part of the business responsible for it.&lt;/p&gt;

&lt;p&gt;For this scenario, create two cost centers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Business&lt;/strong&gt;, containing the relevant Business users or teams.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developers&lt;/strong&gt;, containing the relevant engineering users or teams.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GitHub can then determine which eligible Copilot licenses are attributable to each cost center. Conceptually, the relationship is:&lt;/p&gt;

&lt;p&gt;Cost-center included credits= ∑(included credits from eligible licenses attributed to that cost center)&lt;/p&gt;

&lt;p&gt;This is accounting attribution, not a second license purchase. The enterprise still owns and manages the seats centrally. The cost center tells GitHub where the associated usage and included-credit entitlement belong for governance purposes.&lt;/p&gt;

&lt;p&gt;With that ownership structure in place, GitHub can tell which licenses are attributable to Business and which are attributable to Developers. Ownership is now clear, but both groups can still participate in the same included-credit pool. That creates the next risk.&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%2Fhpndhkf9mc0hrs6xzdd9.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%2Fhpndhkf9mc0hrs6xzdd9.png" alt="Developers and Business cost centers" width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 2: Developers and Business cost centers&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 3: One group can consume another group's included credits
&lt;/h2&gt;

&lt;p&gt;By default, included AI credits can function as a shared enterprise resource. That is convenient, but it can produce an uneven outcome: one group may consume included credits funded by licenses associated with another group.&lt;/p&gt;

&lt;p&gt;Imagine Developers has an unusually intensive month. Without a separate boundary, its members may continue drawing from the shared pool, reducing the included credits available to Business. Attribution tells us who owns the usage, but attribution alone does not protect either group's share.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution: Enable the included usage cap
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;AI credit included usage cap&lt;/strong&gt; changes that behavior for a cost center. When enabled, GitHub calculates an included-credit boundary from the eligible licenses attributable to that cost center.&lt;/p&gt;

&lt;p&gt;For example, enable the checkbox for both Business and Developers. Each cost center can then use the included credits calculated from its attributable licenses without the other cost center consuming beyond its own boundary.&lt;/p&gt;

&lt;p&gt;The safest way to describe this is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The cost center receives a protected included-usage boundary calculated from its attributable eligible licenses.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is tempting to call those credits "guaranteed to me," but that wording can imply more than the control provides. The boundary belongs to the cost center, not to an individual, and it does not guarantee that every member receives an equal allocation.&lt;/p&gt;

&lt;p&gt;The shared-pool problem is now addressed, but the checkbox also exposes the next question: what happens after a cost center exhausts its protected included credits? The cap separates included usage; it does not define the paid-usage policy.&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%2Fe89jyoq7s1apqez8o92m.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%2Fe89jyoq7s1apqez8o92m.png" alt="Included usage cap" width="651" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 3: Included usage cap checked on a cost center&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 4: The included usage cap does not stop overage
&lt;/h2&gt;

&lt;p&gt;Once a cost center reaches its included-credit boundary, additional eligible usage may become paid usage when paid AI credit usage is enabled. A cost-center budget determines how that overage is monitored or stopped.&lt;/p&gt;

&lt;p&gt;Why is a separate budget necessary? The included usage cap says, "Do not continue consuming included credits beyond this cost center's calculated boundary." It does not necessarily say, "Block all subsequent usage." A spending control is required to define that second outcome.&lt;/p&gt;

&lt;p&gt;Our two cost centers need different outcomes. Business should stop before overage, while Developers should be allowed to continue so the enterprise can observe real demand.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution for Business: Use a $0 hard budget
&lt;/h3&gt;

&lt;p&gt;Business should use its included credits but create no overage. Configure a &lt;strong&gt;$0 cost-center budget&lt;/strong&gt; and enable &lt;strong&gt;Stop usage when budget limit is reached&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Together, the controls mean:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Business uses the included credits associated with its attributable licenses.&lt;/li&gt;
&lt;li&gt;The included usage cap prevents it from drawing beyond its protected included boundary.&lt;/li&gt;
&lt;li&gt;The $0 hard budget allows no paid usage after included credits are exhausted.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The $0 budget does not prevent Business from using included credits. It establishes a zero-dollar allowance specifically for the paid-usage phase.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; If paid AI credit usage is disabled for the entire enterprise, a $0 cost-center budget may be redundant. It becomes important in this scenario because Developers must retain access to paid usage under the same enterprise account.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;!Business cost center with a $0 hard budget](&lt;a href="https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/fw05ktbkv415rph8jm5u.png" rel="noopener noreferrer"&gt;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/fw05ktbkv415rph8jm5u.png&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 4: Business cost center with a $0 hard budget&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution for Developers: Start with a soft budget
&lt;/h3&gt;

&lt;p&gt;Developers need more flexibility. Configure a funded cost-center budget, such as &lt;strong&gt;$20,000&lt;/strong&gt;, but leave &lt;strong&gt;Stop usage when budget limit is reached&lt;/strong&gt; disabled.&lt;/p&gt;

&lt;p&gt;This is a soft budget. It provides a target and supports alerts, but it is not a hard ceiling. Usage can continue beyond $20,000 unless another applicable control stops it.&lt;/p&gt;

&lt;p&gt;That behavior is useful while the organization learns the team's real demand. Administrators can monitor spending, review whether the usage produces value, and later decide whether to change the amount or turn on the stop control.&lt;/p&gt;

&lt;p&gt;At this point, Business and Developers have distinct overage policies:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cost center&lt;/th&gt;
&lt;th&gt;Included usage&lt;/th&gt;
&lt;th&gt;Paid-usage budget&lt;/th&gt;
&lt;th&gt;Stop usage&lt;/th&gt;
&lt;th&gt;Outcome&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Business&lt;/td&gt;
&lt;td&gt;Protected boundary enabled&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Use included credits, then stop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Developers&lt;/td&gt;
&lt;td&gt;Protected boundary enabled&lt;/td&gt;
&lt;td&gt;$20,000&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Use included credits, then allow monitored paid usage&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We have now defined what paid usage means for each cost center. However, the Developers budget controls the group total, not the behavior of each person inside the group. One heavy user could still consume a disproportionate amount, which leads to the next problem.&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%2Feqxhkmyldje8lb7nuwep.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%2Feqxhkmyldje8lb7nuwep.png" alt="Developers cost center with a $20,000 soft budget and no stop control" width="530" height="900"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 5: Developers cost center with a $20,000 soft budget and no stop control&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 5: An aggregate budget does not create individual fairness
&lt;/h2&gt;

&lt;p&gt;The $20,000 Developers budget gives administrators visibility into aggregate paid usage, but it does not divide that amount fairly among the people in the cost center. A few heavy users could consume most of the available capacity while everyone else remains far below the group budget.&lt;/p&gt;

&lt;p&gt;Why add a ULB when Developers already has a $20,000 budget? The two controls operate at different levels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;$20,000 cost-center budget&lt;/strong&gt; monitors the Developers group's aggregate paid usage.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;cost-center ULB&lt;/strong&gt; gives each person in Developers an individual ceiling.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Solution: Add a cost-center ULB
&lt;/h3&gt;

&lt;p&gt;A user-level budget limits one person's total AI credit consumption during the billing cycle. It follows the user across included and paid usage and acts as a hard stop when the applicable limit is reached.&lt;/p&gt;

&lt;p&gt;For example, configure a &lt;strong&gt;$200-per-user cost-center ULB&lt;/strong&gt; for Developers. This prevents a small number of heavy users from consuming a disproportionate amount while other users receive little opportunity to work.&lt;/p&gt;

&lt;p&gt;The $200 value is a maximum, not a reservation. It does not set aside $200 for every person, and unused capacity from one user is not a personal entitlement that another user can claim. It simply says that each covered user stops when their individual consumption reaches $200.&lt;/p&gt;

&lt;p&gt;This makes the policy more predictable and equitable without requiring administrators to create a separate budget for every member of the cost center. The common baseline solves the fairness problem, but a uniform limit can be too restrictive for specialized roles.&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%2Fedqsjn9nittb9q5av75w.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%2Fedqsjn9nittb9q5av75w.png" alt="Developers cost center with a $200 per-user ULB" width="519" height="902"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 6: Developers cost center with a $200 per-user ULB&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 6: One baseline does not fit every role
&lt;/h2&gt;

&lt;p&gt;A shared baseline will not fit every role. A platform engineer, AI lead, or approved power user may have a legitimate need for more capacity than the Developers baseline permits.&lt;/p&gt;

&lt;p&gt;Raising the $200 limit for the entire cost center would solve that person's problem by giving everyone more capacity. That is broader than necessary and weakens the fairness policy we just established.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution: Add an individual override
&lt;/h3&gt;

&lt;p&gt;For example, create an individual ULB of &lt;strong&gt;$400&lt;/strong&gt; for &lt;code&gt;softchris&lt;/code&gt;. That individual policy takes precedence over the $200 Developers cost-center ULB.&lt;/p&gt;

&lt;p&gt;The precedence is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Individual ULB&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost-center ULB&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Universal ULB&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This lets administrators start with a broad enterprise default, apply a more suitable baseline to a cost center, and reserve individual overrides for documented exceptions.&lt;/p&gt;

&lt;p&gt;An individual override should still be reviewed. More capacity is not automatically better governance; it should correspond to an approved role or business outcome. We have now solved each problem at the narrowest appropriate scope.&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%2Fyw4tcczmnsgp90t6so9m.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%2Fyw4tcczmnsgp90t6so9m.png" alt="Individual ULB override" width="518" height="896"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 7: Individual ULB override for a specific user in the Developers cost center&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Resolution: See the complete control model
&lt;/h2&gt;

&lt;p&gt;Now that each control has been introduced separately, we can connect them into one end-to-end model.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Purchase Copilot seats centrally.&lt;/strong&gt; The enterprise or organization owns the seat pool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assign seats to named users.&lt;/strong&gt; This establishes who holds an eligible Copilot license.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attribute users, teams, or organizations to cost centers.&lt;/strong&gt; This connects licensed activity to Business or Developers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable the included usage cap.&lt;/strong&gt; GitHub calculates a protected included-credit boundary from eligible licenses attributable to each cost center.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set cost-center budgets.&lt;/strong&gt; Business receives a $0 hard budget; Developers receives a $20,000 soft budget.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set a cost-center ULB.&lt;/strong&gt; Developers users receive a $200 individual ceiling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add approved exceptions.&lt;/strong&gt; &lt;code&gt;softchris&lt;/code&gt; receives a $400 individual ULB.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The resulting &lt;strong&gt;Budgets and alerts&lt;/strong&gt; view tells a coherent story:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;Amount&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cost center&lt;/td&gt;
&lt;td&gt;Business&lt;/td&gt;
&lt;td&gt;$0, stop enabled&lt;/td&gt;
&lt;td&gt;Prevent paid overage after included usage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost center&lt;/td&gt;
&lt;td&gt;Developers&lt;/td&gt;
&lt;td&gt;$20,000, stop disabled&lt;/td&gt;
&lt;td&gt;Observe aggregate paid usage without an immediate hard stop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User • Cost Center&lt;/td&gt;
&lt;td&gt;Developers&lt;/td&gt;
&lt;td&gt;$200 per user&lt;/td&gt;
&lt;td&gt;Apply a fair individual baseline across the cost center&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User&lt;/td&gt;
&lt;td&gt;&lt;code&gt;softchris&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$400&lt;/td&gt;
&lt;td&gt;Preserve an approved individual exception&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These four rows do not show the included usage caps themselves; those are configured on the cost-center details. The rows show the controls that govern paid usage and individual consumption after the attribution model has been established.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checkpoint: Avoid the most common misunderstandings
&lt;/h2&gt;

&lt;p&gt;The controls become easier to operate when their boundaries are explicit. Keep these distinctions in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Purchasing 400 seats does not automatically license an unspecified 400 people. Seats must be assigned to users.&lt;/li&gt;
&lt;li&gt;Putting 200 people in a cost center does not mean 200 licenses contribute to its included-credit calculation. Only attributable users with eligible licenses contribute.&lt;/li&gt;
&lt;li&gt;An included usage cap does not assign an equal number of credits to every person.&lt;/li&gt;
&lt;li&gt;An included usage cap does not, by itself, define the cost center's paid-usage policy.&lt;/li&gt;
&lt;li&gt;A soft cost-center budget is an observation and alerting threshold, not a hard ceiling.&lt;/li&gt;
&lt;li&gt;A cost-center ULB is a per-user maximum, not a guaranteed allocation for each person.&lt;/li&gt;
&lt;li&gt;An individual ULB overrides a broader cost-center or universal ULB for that user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The easiest way to remember the model is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Assign the license. Attribute the usage. Protect included credits. Govern paid usage. Limit the individual.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Outcome: Different teams, appropriate controls
&lt;/h2&gt;

&lt;p&gt;Business and Developers now operate under the same enterprise purchase but follow policies suited to their work.&lt;/p&gt;

&lt;p&gt;Business can consume the included credits associated with its attributable licenses and then stops before creating paid usage. Developers can continue into paid usage while administrators observe demand against a soft budget. A cost-center ULB prevents a few users from dominating consumption, while individual overrides preserve approved exceptions.&lt;/p&gt;

&lt;p&gt;No single checkbox provides all of that behavior. The result comes from combining license assignment, cost-center attribution, included-credit boundaries, spending budgets, and ULBs in the right order.&lt;/p&gt;

&lt;p&gt;That order is the practical governance lesson: &lt;strong&gt;protect included usage first, decide how paid usage should behave second, and then add per-user controls where fairness or predictability requires them.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>github</category>
      <category>billing</category>
      <category>ai</category>
    </item>
    <item>
      <title>GitHub Admin UI + Billing API: Better together for smarter spend decisions</title>
      <dc:creator>Chris Noring</dc:creator>
      <pubDate>Mon, 10 Aug 2026 14:42:20 +0000</pubDate>
      <link>https://dev.to/azure/github-admin-ui-billing-api-better-together-for-smarter-spend-decisions-3lp5</link>
      <guid>https://dev.to/azure/github-admin-ui-billing-api-better-together-for-smarter-spend-decisions-3lp5</guid>
      <description>&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/6DrBG4tP2ms"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;As a GitHub administrator, you already have a strong place to start when somebody asks, “Why did our AI spend go up?” In &lt;strong&gt;Metered usage&lt;/strong&gt;, you can see the change, choose the period, and group the data by organization or cost center.&lt;/p&gt;

&lt;p&gt;That first investigation often leads to questions that are specific to your company. Finance may want a month-end report based on its own reporting calendar. An engineering leader may want to see whether an increase is spread across a team or concentrated among a few people. Answering those questions once is useful; answering them repeatedly calls for a reusable approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Series
&lt;/h2&gt;

&lt;p&gt;This is part of a series on GitHub Admin and billing controls.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/azure/turning-surprise-ai-bills-into-accountable-growth-4343"&gt;Managing surprise bills&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Better together: GitHub Admin UI + Billing, &lt;strong&gt;we're here&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/azure/understanding-github-billing-and-management-from-licenses-to-fair-ai-credit-controls-1hli/"&gt;Understanding GitHub Billing and management&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use each surface for what it does best
&lt;/h2&gt;

&lt;p&gt;The GitHub admin UI shows you where to look and gives you the controls to respond. The Billing Usage API helps you answer the recurring questions that are specific to your company. Neither replaces the other.&lt;/p&gt;

&lt;p&gt;Together, they give administrators a practical loop: spot the change in &lt;strong&gt;Metered usage&lt;/strong&gt;, understand it through a reusable API-powered view, and act with a targeted budget. That means better cost control without treating every user or team as the problem.&lt;/p&gt;

&lt;p&gt;Let’s walk through this better-together approach using a common example: AI spend starts to rise, but the reason is not yet clear.&lt;/p&gt;

&lt;h2&gt;
  
  
  The question: Spend is up, but what is driving it?
&lt;/h2&gt;

&lt;p&gt;Imagine that finance notices an increase in AI spend before the next close. It could be a sign that more developers are getting value from Copilot. It could also be one workload using far more than expected. At this point, nobody knows, and a broad restriction would be premature.&lt;/p&gt;

&lt;p&gt;The GitHub administrator needs to help finance and engineering answer three practical questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which part of the business is driving the increase?&lt;/li&gt;
&lt;li&gt;Is the spend concentrated among a few users or broadly distributed?&lt;/li&gt;
&lt;li&gt;Which control should change without disrupting everyone else?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not simply to reduce a number. It is to understand the increase well enough to protect useful work while addressing anything unexpected.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Start in the admin UI: Find the increase
&lt;/h3&gt;

&lt;p&gt;The admin UI is the natural place to begin because it lets you explore the data before you decide what kind of report or control you need. Open &lt;strong&gt;Billing and licensing &amp;gt; Metered usage&lt;/strong&gt; and select the relevant reporting period.&lt;/p&gt;

&lt;p&gt;This first check matters. It confirms that the increase is real, shows when it happened, and gives you a shared starting point for the conversation with finance and engineering.&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%2Fsh65xqssc6t2qgmwjow9.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%2Fsh65xqssc6t2qgmwjow9.png" alt="Metered usage establishes the increase and the period that needs investigation." width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 01: Metered usage establishes the increase and the period that needs investigation.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Narrow the increase by organization
&lt;/h4&gt;

&lt;p&gt;An enterprise total tells you that spend changed, but not where to look next. Group the usage by organization to see which part of the enterprise contributed most to the increase.&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%2Fz4p44wy4z6z8fuss7rv2.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%2Fz4p44wy4z6z8fuss7rv2.png" alt="Organization grouping narrows an enterprise-wide increase to an accountable business area" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 02: Organization grouping narrows an enterprise-wide increase to an accountable business area.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Suppose the &lt;code&gt;octodemo&lt;/code&gt; organization stands out. You now know where to continue the investigation and which leaders can add context. You do not yet know whether the spend is justified, and that distinction matters. The increase could come from successful Copilot adoption, a migration, a seasonal workload, or an automated process that needs attention.&lt;/p&gt;

&lt;h4&gt;
  
  
  Connect the increase to a cost center
&lt;/h4&gt;

&lt;p&gt;An organization can contain several teams, programs, and budgets. Grouping by &lt;strong&gt;cost center&lt;/strong&gt; takes the investigation one step closer to the people who understand the work behind the spend.&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%2F1eoius8ej9nyriha738v.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%2F1eoius8ej9nyriha738v.png" alt="Cost-center grouping identifies the financial owner of the increase" width="800" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 03: Cost-center grouping identifies the financial owner of the increase.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this scenario, &lt;code&gt;octodemo-org-cc&lt;/code&gt; has the largest increase. In only a few clicks, the admin UI has taken us from an enterprise-wide signal to the cost center that needs a closer look. For a one-time question, this may be enough.&lt;/p&gt;

&lt;p&gt;Now imagine that finance asks for the same analysis every month, with a fixed reporting period and a ranking of spend by user. That is the point where the API adds value. It does not replace the investigation you just completed; it helps you repeat and extend it.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Continue with the API: Answer the repeatable question
&lt;/h3&gt;

&lt;p&gt;The Billing Usage API gives you access to the data behind a more tailored report. You can use filters to match the period finance cares about, focus on the cost center you found in the UI, and build a view that can run again tomorrow or next month.&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%2Fjj8vs3z0ht9qyjwg98h5.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%2Fjj8vs3z0ht9qyjwg98h5.png" alt="Billing usage endpoints and time filters provide the inputs for a reusable report" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 04: Billing usage endpoints and time filters provide the inputs for a reusable report.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Define the reporting question first
&lt;/h4&gt;

&lt;p&gt;Before writing code, state the question the report needs to answer. In this example, it is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which users in the selected cost center account for the most net spend during this reporting period?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That one question keeps the report focused. It also determines the workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;List the organization's members to establish the candidate users.&lt;/li&gt;
&lt;li&gt;Resolve which members belong to the selected cost center.&lt;/li&gt;
&lt;li&gt;Query organization AI credit and premium-request usage for those users and the selected period.&lt;/li&gt;
&lt;li&gt;Combine the results into a per-user total.&lt;/li&gt;
&lt;li&gt;Rank users and aggregate the result by cost center.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The prototype uses &lt;code&gt;year&lt;/code&gt;, &lt;code&gt;month&lt;/code&gt;, and optional &lt;code&gt;day&lt;/code&gt; filters so the output matches the finance period. It also accepts a cost-center filter. Because the admin UI has already pointed us to &lt;code&gt;octodemo-org-cc&lt;/code&gt;, there is no reason to start with every member of the enterprise.&lt;/p&gt;

&lt;h4&gt;
  
  
  Understand the per-user query pattern
&lt;/h4&gt;

&lt;p&gt;There is one API behavior to understand before building the report. The organization billing endpoints return an aggregate when the &lt;code&gt;user&lt;/code&gt; filter is omitted. To create a spend-by-user ranking, the workflow makes a filtered request for each selected user and usage type.&lt;/p&gt;

&lt;p&gt;For example, this request asks for Eve's AI credit usage in July 2026:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Accept: application/vnd.github+json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$GITHUB_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-GitHub-Api-Version: 2026-03-10"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"https://api.github.com/organizations/octodemo/settings/billing/ai_credit/usage?year=2026&amp;amp;month=7&amp;amp;user=eve"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response contains one or more usage items, with amounts such as &lt;code&gt;grossAmount&lt;/code&gt;, &lt;code&gt;discountAmount&lt;/code&gt;, and &lt;code&gt;netAmount&lt;/code&gt;. The prototype adds the &lt;code&gt;netAmount&lt;/code&gt; values to calculate Eve's AI credit total for the period. It then runs the equivalent premium-request query and combines the two totals.&lt;/p&gt;

&lt;p&gt;We can now see one user's contribution during the same period we investigated in the UI. Repeating the request for the members of the selected cost center gives us the ranking that finance asked for.&lt;/p&gt;

&lt;p&gt;For a production workflow, a few practical details matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Limit the candidate list to the cost center under investigation.&lt;/li&gt;
&lt;li&gt;Paginate organization membership and cost-center results.&lt;/li&gt;
&lt;li&gt;Use bounded concurrency instead of sending every request at once.&lt;/li&gt;
&lt;li&gt;Record partial failures rather than silently treating them as zero spend.&lt;/li&gt;
&lt;li&gt;Keep an audit record of when the data was pulled and transformed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a daily check, the report can use a narrow period and write a timestamped output. At finance close, the same workflow can produce the month-end rollup. The question stays the same; only the reporting window changes.&lt;/p&gt;

&lt;h4&gt;
  
  
  Reveal concentration that totals can hide
&lt;/h4&gt;

&lt;p&gt;The result is a custom &lt;strong&gt;Spend by User&lt;/strong&gt; view that brings the organization, cost center, reporting period, AI credit usage, premium-request usage, and total net spend into one place.&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%2Fve88hld9bbwqxy37zhok.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%2Fve88hld9bbwqxy37zhok.png" alt="Custom Spend by User dashboard" width="799" height="701"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 05: A company-specific dashboard exposes per-user concentration inside the selected cost center.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the illustrative data, the &lt;code&gt;octodemo&lt;/code&gt; organization has 22 users and $3,651 in total net spend for July 2026. The &lt;code&gt;octodemo-org-cc&lt;/code&gt; cost center accounts for $2,700 of that amount. Two users stand out:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;User&lt;/th&gt;
&lt;th&gt;AI credit net spend&lt;/th&gt;
&lt;th&gt;Premium-request net spend&lt;/th&gt;
&lt;th&gt;Total net spend&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;eve&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$900&lt;/td&gt;
&lt;td&gt;$600&lt;/td&gt;
&lt;td&gt;$1,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;adam&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$600&lt;/td&gt;
&lt;td&gt;$400&lt;/td&gt;
&lt;td&gt;$1,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Together, Adam and Eve account for $2,500 of the $2,700 attributed to that cost center. That is approximately 93% of its total in this example.&lt;/p&gt;

&lt;p&gt;These figures are demonstration data, but they show why the extra view is useful. Instead of reacting to a $2,700 cost-center total, the administrator can talk to the owners of two workloads and understand what the spend supported.&lt;/p&gt;

&lt;p&gt;Concentration does not automatically mean waste. Adam and Eve may be doing approved, high-value work. The dashboard tells the business where to ask the next question; the people involved provide the context needed to answer it.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Return to the admin UI: Choose the right control
&lt;/h3&gt;

&lt;p&gt;The API has helped us understand the increase, but it does not make the decision for us. Return to &lt;strong&gt;Billing and licensing &amp;gt; Budgets and alerts&lt;/strong&gt; to review the available controls and choose the narrowest one that fits what you learned.&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%2F946qzr0ow83ensccroqe.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%2F946qzr0ow83ensccroqe.png" alt="Budget scopes turn the investigation into a targeted governance decision" width="515" height="699"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 06: Budget scopes turn the investigation into a targeted governance decision.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Set a cost-center user-level baseline
&lt;/h4&gt;

&lt;p&gt;A cost-center user-level budget applies the same per-user amount to every current and future member of that cost center. This is useful when the group needs a different baseline from the rest of the enterprise.&lt;/p&gt;

&lt;p&gt;For example, the administrator might give &lt;code&gt;octodemo-org-cc&lt;/code&gt; additional per-user headroom because its work legitimately uses more AI credits. This avoids raising the universal user-level budget for everyone.&lt;/p&gt;

&lt;p&gt;A user-level budget counts both included and paid AI credit usage. It is always a hard stop for the individual. It does not reserve part of the shared pool, and it does not replace the cost center's paid-usage budget.&lt;/p&gt;

&lt;h4&gt;
  
  
  Preserve justified exceptions
&lt;/h4&gt;

&lt;p&gt;If Adam or Eve has an approved role that requires more capacity, an individual user-level budget can replace the cost-center baseline for that person. The exception stays limited to the person who needs it instead of increasing the budget for the whole cost center.&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%2Fd0j8iyq3wikvamk6kme2.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%2Fd0j8iyq3wikvamk6kme2.png" alt=" " width="525" height="879"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 07: Cost-center baselines and individual overrides preserve useful work without widening access for everyone.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The precedence is straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An individual user-level budget overrides the cost-center user-level budget.&lt;/li&gt;
&lt;li&gt;The cost-center user-level budget overrides the universal user-level budget.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In practice, you can set a universal baseline, add more headroom for a cost center with a clear business need, and use individual overrides for documented exceptions.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why the UI and API work better together
&lt;/h4&gt;

&lt;p&gt;At this point, the better-together pattern becomes clear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Metered usage&lt;/strong&gt; supports interactive discovery.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Billing Usage API&lt;/strong&gt; supports repeatable, company-specific analysis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Budgets and alerts&lt;/strong&gt; supports targeted policy decisions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each surface does the job it is best suited to do. The UI makes it easy to explore and manage GitHub. The API lets you repeat a company-specific analysis without rebuilding it by hand. Used together, they give finance, engineering, and administrators the same evidence before a control changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make it part of the operating rhythm
&lt;/h2&gt;

&lt;p&gt;A useful dashboard should lead to a useful conversation. Decide who receives the report, how often they review it, and what happens when a user or cost center stands out.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run a daily pull to detect unusual changes early.&lt;/li&gt;
&lt;li&gt;Produce a month-end rollup aligned to finance close.&lt;/li&gt;
&lt;li&gt;Route cost-center summaries to the relevant business owner.&lt;/li&gt;
&lt;li&gt;Review high-consumption users with engineering before changing limits.&lt;/li&gt;
&lt;li&gt;Record approved individual overrides and revisit them regularly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Over time, the conversation can move from “Who spent this?” to “What outcome did this spend support, and does the current policy still fit?”&lt;/p&gt;

&lt;p&gt;When the same users repeatedly appear at the top, leaders can inspect the workload, remove waste, validate business value, or approve more capacity. When usage becomes broadly distributed, the cost-center baseline may need adjustment instead. The report makes those patterns visible over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  The better-together workflow at a glance
&lt;/h3&gt;

&lt;p&gt;The story above introduces each surface when it becomes useful. This table summarizes their roles.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Surface&lt;/th&gt;
&lt;th&gt;Primary role&lt;/th&gt;
&lt;th&gt;Best used for&lt;/th&gt;
&lt;th&gt;Important limitation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Metered usage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Interactive investigation&lt;/td&gt;
&lt;td&gt;Finding the affected period, organization, and cost center&lt;/td&gt;
&lt;td&gt;Manual exploration is not a reusable company-specific report&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Billing Usage API&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Programmatic usage retrieval&lt;/td&gt;
&lt;td&gt;Scheduled reporting, time-sliced analysis, and per-user views&lt;/td&gt;
&lt;td&gt;Per-user attribution requires filtered requests and careful handling of pagination and failures&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Custom Spend by User view&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Company-specific interpretation&lt;/td&gt;
&lt;td&gt;Ranking users and aligning usage to internal ownership&lt;/td&gt;
&lt;td&gt;Concentration is evidence to investigate, not proof of waste&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Budgets and alerts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Governance controls&lt;/td&gt;
&lt;td&gt;Cost-center baselines and individual overrides&lt;/td&gt;
&lt;td&gt;A broader budget cannot override a user who has reached their ULB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The practical takeaway is simple: begin with exploration, automate only the question worth repeating, and adjust policy after the data has context. That sequence keeps governance precise while preserving useful AI work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn more
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/rest/billing/usage?apiVersion=2026-03-10" rel="noopener noreferrer"&gt;REST API endpoints for billing usage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/rest/orgs/members?apiVersion=2026-03-10#list-organization-members" rel="noopener noreferrer"&gt;List organization members&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/enterprise-cloud@latest/copilot/concepts/billing/budgets-for-usage-based-billing" rel="noopener noreferrer"&gt;Budgets for usage-based billing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/enterprise-cloud@latest/billing/how-tos/products/use-cost-centers" rel="noopener noreferrer"&gt;Using cost centers to allocate costs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>github</category>
      <category>billing</category>
    </item>
    <item>
      <title>Turning surprise AI bills into accountable growth with GitHub Billing Controls</title>
      <dc:creator>Chris Noring</dc:creator>
      <pubDate>Mon, 27 Jul 2026 18:44:11 +0000</pubDate>
      <link>https://dev.to/azure/turning-surprise-ai-bills-into-accountable-growth-4343</link>
      <guid>https://dev.to/azure/turning-surprise-ai-bills-into-accountable-growth-4343</guid>
      <description>&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/4fxSmUQV7qc"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This is a story of how you receive a higher bill than expected and how GitHub's Billing Controls help clarity and predictability. Let's begin our story.&lt;/p&gt;

&lt;h2&gt;
  
  
  Series
&lt;/h2&gt;

&lt;p&gt;This is part of a series on GitHub Admin and billing controls.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Managing surprise bills, &lt;strong&gt;you're here&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/azure/github-admin-ui-billing-api-better-together-for-smarter-spend-decisions-3lp5"&gt;Better together: GitHub Admin UI + Billing API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/azure/understanding-github-billing-and-management-from-licenses-to-fair-ai-credit-controls-1hli/"&gt;Understanding GitHub Billing and management&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Confusion: The bill is higher than expected
&lt;/h2&gt;

&lt;p&gt;The AI bill is higher than expected. Now what?&lt;/p&gt;

&lt;p&gt;Finance wants to understand what is driving the cost. Engineering leaders, on the other hand, want to preserve the productivity gains behind the increased usage. The administrator needs to balance both priorities and put a policy in place that the business can understand.&lt;/p&gt;

&lt;p&gt;One question drives the investigation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where is the increase coming from, and how do we control it without disrupting valuable work?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To answer it, we first need to follow the spend. Once we know who owns it, we can apply guardrails at the right level.&lt;/p&gt;

&lt;h2&gt;
  
  
  Investigation: Follow the spend
&lt;/h2&gt;

&lt;p&gt;Setting a limit too early could restrict useful adoption without addressing the main source of cost. So, let's find out what changed and who can act on it.&lt;/p&gt;

&lt;p&gt;The investigation begins in the billing administration portal, where usage and budget settings appear in one place.&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%2Fakw88c0tldf9dtip9tws.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%2Fakw88c0tldf9dtip9tws.png" alt="GitHub Billing Administration Portal" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 01: A unified billing workspace connects usage evidence to budget controls.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Identify the consumption category
&lt;/h3&gt;

&lt;p&gt;First, we identify which product changed. GitHub reports this by &lt;strong&gt;SKU&lt;/strong&gt;, which simply means the billing category for a product or service.&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%2F2p3pxtew37qb4jknaxok.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%2F2p3pxtew37qb4jknaxok.png" alt="Metered usage" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 02: Grouping metered usage by billing category highlights Copilot Enterprise usage.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Grouping usage by billing category shows which product is driving the increase. In this example, the chart points to Copilot Enterprise usage. Leaders can then ask whether that growth comes from valuable adoption, an unusual workload, or demand that has outgrown its budget.&lt;/p&gt;

&lt;h3&gt;
  
  
  Locate the accountable organization
&lt;/h3&gt;

&lt;p&gt;Once you know which product is driving the cost, find out who owns the usage. An enterprise-wide total can hide a sharp increase in one organization.&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%2F8hrk3ck5y5rktx2qp1z2.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%2F8hrk3ck5y5rktx2qp1z2.png" alt="Usage grouped by organization" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 03: Organization-level grouping identifies the business unit accountable for demand.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The organization view points to the leader who can connect that usage to business results, estimate future demand, and decide what to do next.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect usage to a cost center
&lt;/h3&gt;

&lt;p&gt;An organization may still be too broad. A &lt;strong&gt;cost center&lt;/strong&gt; is a billing group that connects usage to the team, program, or function responsible for the cost.&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%2F0hr1rb7298oaxtvrrz07.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%2F0hr1rb7298oaxtvrrz07.png" alt="Group by cost center" width="800" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 04: Cost-center analysis reveals concentrated usage averaging about $20,000 per month.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here, one cost center accounts for most of the AI credit usage and averages about $20,000 per month. That gives leaders a practical starting point. A $20,000 alert may suit normal demand, while a growth team, seasonal workload, or strategic migration may need more &lt;strong&gt;headroom&lt;/strong&gt;, or more room to spend before reaching the limit. The right amount depends on expected usage, financial risk, and the value the work creates.&lt;/p&gt;

&lt;p&gt;The investigation has now produced an answer: most of the increase comes from one cost center spending about $20,000 per month. The next question is what to do about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resolution: Put guardrails in place
&lt;/h2&gt;

&lt;p&gt;Rather than place one restrictive limit on everyone, the administrator can add guardrails at three levels: across the organization, for the cost center driving the increase, and for individual users who need a different limit.&lt;/p&gt;

&lt;p&gt;The Budgets and alerts page is where those decisions become policy.&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%2F0zdzaxgewn7dq0ow719r.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%2F0zdzaxgewn7dq0ow719r.png" alt="Budgets and alerts in Billing and licensing" width="796" height="103"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 05: The Budgets and alerts page provides the New budget action.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's start with the broadest guardrail and then make it more specific.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set a boundary for shared paid usage
&lt;/h3&gt;

&lt;p&gt;An organization budget gives finance a clear limit for usage charges. License costs are separate and do not count toward this amount.&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%2Fsowopktuevrzb54r0k56.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%2Fsowopktuevrzb54r0k56.png" alt="Organization budget" width="544" height="896"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 06: A $200,000 organization budget establishes a configurable boundary for paid usage.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The $200,000 organization budget shown here is an example. The right amount depends on expected usage, available included credits, growth plans, and what would happen if paid usage stopped. This shared budget applies only to additional paid usage after included credits are used. It does not include license costs.&lt;/p&gt;

&lt;p&gt;There is one timing detail to keep in mind: if you create the budget partway through a billing cycle, GitHub does not count usage from earlier in that cycle. The first bill can therefore exceed the displayed $200,000 limit.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Stop usage when budget limit is reached&lt;/strong&gt; toggle controls what happens next. Leave it off and GitHub sends notifications while usage continues. Turn it on and affected paid usage stops at the limit. GitHub Copilot code completions and next edit suggestions still work because they do not use this paid AI credit allowance. A blocked request also does not automatically switch to a cheaper model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create accountable room for a cost center
&lt;/h3&gt;

&lt;p&gt;The organization budget covers shared paid usage across the organization. A cost-center budget gives a specific team an earlier warning.&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%2Fknal0tnmghrqw6m4bkow.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%2Fknal0tnmghrqw6m4bkow.png" alt="Cost center budget" width="515" height="699"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 07: A $20,000 cost-center threshold monitors current demand without stopping usage.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this example, the administrator sets a $20,000 alert and leaves the stop toggle off. The owner can review the workload, remove waste, or ask for a higher limit before usage is interrupted.&lt;/p&gt;

&lt;p&gt;The same timing rule applies here: if the budget is created partway through the billing cycle, earlier usage is not counted. The first bill can therefore exceed the displayed $20,000 limit.&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%2Fobyyabok6aoe401usz2u.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%2Fobyyabok6aoe401usz2u.png" alt="Budget alert configuration" width="543" height="701"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 08: Threshold alerts route emerging cost risk to owners before month-end.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Alerts give people time to act. Finance can review expected spending, the cost-center owner can explain the increase, and the administrator can adjust the limit or turn on stopping if needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Give different users the right limits
&lt;/h3&gt;

&lt;p&gt;Shared budgets do not control how many AI credits one person can use. Per-user budgets do. They count both included and paid credits, and they always stop further AI-credit use when the person's limit is reached.&lt;/p&gt;

&lt;p&gt;Set these limits from broad to specific:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Establish a universal per-user &lt;strong&gt;baseline&lt;/strong&gt;, the starting limit for every licensed user.&lt;/li&gt;
&lt;li&gt;Add a &lt;strong&gt;cost-center exception&lt;/strong&gt; where a group's expected outcomes justify a different limit.&lt;/li&gt;
&lt;li&gt;Add an individual &lt;strong&gt;override&lt;/strong&gt;, a replacement limit for one person, only for a role with a documented need.&lt;/li&gt;
&lt;/ol&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%2Ftwd2ti7jzjnhyv8wmh3z.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%2Ftwd2ti7jzjnhyv8wmh3z.png" alt="Cost-center per-user budget" width="493" height="641"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 09: A $200 per-user cost-center exception adds targeted headroom without raising the enterprise baseline.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The screenshot shows the second step: a $200 per-user limit for one cost center. Start with the universal baseline, then add an individual override only when someone has a clear need for a different amount. This keeps exceptions easy to review and avoids raising the limit for everyone just to support one specialist.&lt;/p&gt;

&lt;h3&gt;
  
  
  Explain which user limit takes priority
&lt;/h3&gt;

&lt;p&gt;What happens when several limits cover the same user? &lt;strong&gt;Precedence&lt;/strong&gt; is simply the order GitHub follows to choose the user-level budget that applies.&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%2Fa9h7c4id3tqd6e6op9km.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%2Fa9h7c4id3tqd6e6op9km.png" alt="Precedence" width="493" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fig 10: Policy precedence applies individual overrides before cost-center and universal user limits.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Among &lt;strong&gt;user-level budgets&lt;/strong&gt;, the individual override comes first, followed by the cost-center per-user budget and then the universal baseline. For example, imagine a $100 universal baseline, a $200 cost-center limit, and a $500 individual override. The specialist gets $500, colleagues in that cost center get $200 each, and everyone else gets $100 each.&lt;/p&gt;

&lt;p&gt;Shared organization and cost-center limits still apply separately. Whichever relevant limit is reached first blocks further usage. A person may still have money left in an individual budget when the shared limit has already been reached. The reverse can also happen: a person's limit may stop their usage while the shared budget still has room.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aftermath: Keep the policy working
&lt;/h2&gt;

&lt;p&gt;Setting the limits is only the start. Someone still needs to review what happens next.&lt;/p&gt;

&lt;p&gt;Finance can see where paid usage is limited. Business owners can see which costs they own. Engineering leaders can see where exceptions protect important work. Included-pool controls protect shared credits, organization and cost-center budgets limit additional paid usage, and per-user budgets set individual limits.&lt;/p&gt;

&lt;p&gt;Assign someone to review alerts, blocked requests, approved exceptions, and business results on a regular schedule. When a team repeatedly reaches its limit, that owner can look for waste, compare usage with results, and recommend a change. Finance and engineering leaders can then approve the change or step in when cost and delivery needs conflict.&lt;/p&gt;

&lt;p&gt;With those responsibilities clear, finance can see the cost, business leaders can own it, and administrators can adjust the controls before the next surprise bill arrives.&lt;/p&gt;

&lt;h3&gt;
  
  
  A quick reference to the controls
&lt;/h3&gt;

&lt;p&gt;The story above introduces each control when it becomes useful. This table collects the technical differences in one place for reference.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Control&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;What it counts&lt;/th&gt;
&lt;th&gt;What happens at the limit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Organization or cost-center budget&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Monitors or limits shared paid usage&lt;/td&gt;
&lt;td&gt;Additional paid usage after included credits are used; license costs are excluded&lt;/td&gt;
&lt;td&gt;Sends alerts, or stops affected paid usage when stopping is enabled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Included AI credit pool&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Protects the credits included with licenses assigned to a cost center&lt;/td&gt;
&lt;td&gt;Shared included AI credits calculated by GitHub&lt;/td&gt;
&lt;td&gt;Blocks more AI-credit use, or allows paid usage when paid usage is enabled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Per-user budget&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limits one person's total AI-credit use&lt;/td&gt;
&lt;td&gt;Included and paid AI credits for that user&lt;/td&gt;
&lt;td&gt;Always stops more affected usage&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When an included AI credit pool runs out, administrators can block further AI-credit use or allow it to continue as paid usage, as long as paid usage is enabled. Some reductions to the pool take effect in the next billing cycle, so check the timing before relying on a lower limit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn more
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.blog/changelog/2026-06-25-assign-enterprise-teams-to-cost-centers/" rel="noopener noreferrer"&gt;Assign enterprise teams to cost centers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.blog/changelog/2026-06-30-per-user-ai-credit-budgets-available-for-cost-centers/" rel="noopener noreferrer"&gt;Per-user AI credit budgets available for cost centers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.blog/changelog/2026-07-02-cost-centers-now-support-included-usage-caps/" rel="noopener noreferrer"&gt;Cost centers now support included usage caps&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>github</category>
      <category>billing</category>
    </item>
    <item>
      <title>Getting started with GitHub Copilot part 2, streamable responses</title>
      <dc:creator>Chris Noring</dc:creator>
      <pubDate>Mon, 16 Mar 2026 20:14:28 +0000</pubDate>
      <link>https://dev.to/azure/getting-started-with-github-copilot-part-2-streamable-responses-49a8</link>
      <guid>https://dev.to/azure/getting-started-with-github-copilot-part-2-streamable-responses-49a8</guid>
      <description>&lt;p&gt;I'm sure you've seen many AI apps where you sit tight for 30s or and you wonder if things are stuck? Not a great experience right? Yes, you're right, you deserve better, so how do we fix it?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Type your prompt&amp;gt; Tell me a joke
.
.
.
.
.
.
.
.
Why could I never find the atoms, cause they split..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/T4p-C2v_0wU"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;h2&gt;
  
  
  Series on Copilot SDK
&lt;/h2&gt;

&lt;p&gt;This series is about Copilot SDK and how you can leverage your existing GitHub Copilot license to integrate AI into your apps&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/azure/get-started-with-github-copilot-sdk-1ijm/"&gt;Part 1 - install and your first app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 2 - streamable response, &lt;strong&gt;you're here&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Addressing the problem
&lt;/h2&gt;

&lt;p&gt;By streaming the response, the response now arrives in chunks, pieces that you can show as soon as they arrive. How can we do that though and how can GitHub Copilot SDK help out?&lt;/p&gt;

&lt;p&gt;Well, there's two things you need to do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable streaming. You need to set &lt;code&gt;streaming&lt;/code&gt; to &lt;code&gt;True&lt;/code&gt; when you call &lt;code&gt;create_session&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Listen for events that contains a chunk. Specifically, you need to listen to &lt;code&gt;ASSISTANT_MESSAGE_DELTA&lt;/code&gt; and print out the chunk.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 1. Enable streaming
&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_session&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;on_permission_request&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PermissionHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;approve_all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;streaming&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Starting streamed response:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Listen for response chunks
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;SessionEventType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ASSISTANT_MESSAGE_DELTA&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mf"&gt;2.&lt;/span&gt; &lt;span class="n"&gt;Chunk&lt;/span&gt; &lt;span class="n"&gt;arrived&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&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="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delta_content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;SessionEventType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SESSION_IDLE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# New line when done
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what the full application looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;copilot&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CopilotClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PermissionHandler&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;copilot.generated.session_events&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SessionEventType&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CopilotClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_session&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;on_permission_request&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PermissionHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;approve_all&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;streaming&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Starting streamed response:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Listen for response chunks
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;SessionEventType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ASSISTANT_MESSAGE_DELTA&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&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="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delta_content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;SessionEventType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SESSION_IDLE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# New line when done
&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handle_event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sending prompt...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_and_wait&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tell me a short joke&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it folks, now go out and build better experiences for your users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/github/copilot-sdk/blob/main/docs/getting-started.md#step-3-add-streaming-responses" rel="noopener noreferrer"&gt;Streamed response&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>githubcopilot</category>
      <category>copilotsdk</category>
      <category>python</category>
    </item>
    <item>
      <title>Don't miss this opportunity to learn more about GitHub Copilot CLI</title>
      <dc:creator>Chris Noring</dc:creator>
      <pubDate>Sun, 08 Mar 2026 22:24:48 +0000</pubDate>
      <link>https://dev.to/softchris/dont-miss-this-opportunity-to-learn-more-about-github-copilot-cli-22dm</link>
      <guid>https://dev.to/softchris/dont-miss-this-opportunity-to-learn-more-about-github-copilot-cli-22dm</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/azure/get-started-with-github-copilot-cli-a-free-hands-on-course-3beg" class="crayons-story__hidden-navigation-link"&gt;Get started with GitHub Copilot CLI: A free, hands-on course&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;
          &lt;a class="crayons-logo crayons-logo--l" href="/azure"&gt;
            &lt;img alt="Microsoft Azure logo" 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%2Forganization%2Fprofile_image%2F512%2F64ce0b82-730d-4ca0-8359-2c21513a0063.jpg" class="crayons-logo__image"&gt;
          &lt;/a&gt;

          &lt;a href="/reneenoble" class="crayons-avatar  crayons-avatar--s absolute -right-2 -bottom-2 border-solid border-2 border-base-inverted  "&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%2Fuser%2Fprofile_image%2F1330299%2Ffc5d4c4e-270f-4fa0-a75c-c6b4e3bfd97c.png" alt="reneenoble profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/reneenoble" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Renee Noble
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Renee Noble
                
              
              &lt;div id="story-author-preview-content-3310566" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/reneenoble" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F1330299%2Ffc5d4c4e-270f-4fa0-a75c-c6b4e3bfd97c.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Renee Noble&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

            &lt;span&gt;
              &lt;span class="crayons-story__tertiary fw-normal"&gt; for &lt;/span&gt;&lt;a href="/azure" class="crayons-story__secondary fw-medium"&gt;Microsoft Azure&lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;a href="https://dev.to/azure/get-started-with-github-copilot-cli-a-free-hands-on-course-3beg" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 5&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/azure/get-started-with-github-copilot-cli-a-free-hands-on-course-3beg" id="article-link-3310566"&gt;
          Get started with GitHub Copilot CLI: A free, hands-on course
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/githubcopilot"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;githubcopilot&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/cli"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;cli&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/vscode"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;vscode&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/terminal"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;terminal&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/azure/get-started-with-github-copilot-cli-a-free-hands-on-course-3beg" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;15&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/azure/get-started-with-github-copilot-cli-a-free-hands-on-course-3beg#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>githubcopilot</category>
      <category>cli</category>
      <category>vscode</category>
      <category>terminal</category>
    </item>
    <item>
      <title>Get started with GitHub Copilot SDK, part 1</title>
      <dc:creator>Chris Noring</dc:creator>
      <pubDate>Wed, 04 Mar 2026 20:48:00 +0000</pubDate>
      <link>https://dev.to/azure/get-started-with-github-copilot-sdk-1ijm</link>
      <guid>https://dev.to/azure/get-started-with-github-copilot-sdk-1ijm</guid>
      <description>&lt;p&gt;Did you know GitHub Copilot now has an SDK and that you can leverage your existing license to build AI integrations into your app? No, well I hope I have you attention now.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/hvwGZqS4qF0"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;h2&gt;
  
  
  Series on Copilot SDK
&lt;/h2&gt;

&lt;p&gt;This series is about Copilot SDK and how you can leverage your existing GitHub Copilot license to integrate AI into your apps&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 1 - install and your first app, &lt;strong&gt;you're here&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/azure/getting-started-with-github-copilot-part-2-streamable-responses-49a8"&gt;Part 2 - streamable responses&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;

&lt;p&gt;You need two pieces here to get started:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Copilot CLI&lt;/li&gt;
&lt;li&gt;A supported runtime, which at present means either Node.js, .NET, Python or Go&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then you need to install the SDK for your chosen runtime like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;github-copilot-sdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The parts
&lt;/h2&gt;

&lt;p&gt;So what do you need to know to get started? There are three concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Client&lt;/strong&gt;, you need to create and instance of it. Additionally you need to start and stop it when you're done with it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session&lt;/strong&gt;. The session takes an object where you can set things like model, system prompt and more. Also, the session is what you talk when you  want to carry out a request. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response&lt;/strong&gt;. The response contains your LLM response.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below is an example program using these three concepts. As you can see we choose "gpt-4.1" as model but this can be changed. See also how we pass the prompt to the function &lt;code&gt;send_and_wait&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;copilot&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CopilotClient&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CopilotClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_session&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_and_wait&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What is 2 + 2?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, now that we know what a simple program looks like, let's make something interesting, an FAQ responder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your first app
&lt;/h2&gt;

&lt;p&gt;An FAQ for a web page, is often a pretty boring read. A way to make that more interesting for the end user is if they can instead chat with the FAQ, let's make that happen. &lt;/p&gt;

&lt;p&gt;Here's the plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define a static FAQ&lt;/li&gt;
&lt;li&gt;Add the FAQ as part of the prompt.&lt;/li&gt;
&lt;li&gt;Make a request to to the LLM and print out the response.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's build out the code little by little. First, let's define the FAQ information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-1- FAQ information&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# faq.py
&lt;/span&gt;
&lt;span class="n"&gt;faq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;warranty&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Our products come with a 1-year warranty covering manufacturing defects. Please contact our support team for assistance.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;return_policy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;We offer a 30-day return policy for unused products in their original packaging. To initiate a return, please visit our returns page and follow the instructions.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shipping&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;We offer free standard shipping on all orders over $50. Expedited shipping options are available at checkout for an additional fee.&lt;/span&gt;&lt;span class="sh"&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;Next, let's add the call to the Copilot SDK&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-2  Adding the LLM call&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;copilot&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CopilotClient&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;faq_to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;faq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;faq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()])&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tell me about shipping&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CopilotClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Here&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s the FAQ, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;faq_to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;faq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;User question: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Answer:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;   

    &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_session&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_and_wait&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;My first app using the GitHub Copilot SDK!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[LOG] Asking the model about shipping information...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tell me about shipping&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note how we concatenate the FAQ data with the user's prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Here&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s the FAQ, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;faq_to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;faq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;User question: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Answer:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;   
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;-3- Let's run it&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv run faq.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see output like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;My first app using the GitHub Copilot SDK!
[LOG] Asking the model about shipping information...
We offer free standard shipping on all orders over $50. Expedited shipping options are available at checkout for an additional fee.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Check out the &lt;a href="https://github.com/github/copilot-sdk/blob/main/docs/getting-started.md" rel="noopener noreferrer"&gt;official docs&lt;/a&gt;&lt;/p&gt;

</description>
      <category>githubcopilot</category>
      <category>python</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>How YOU can build a Web API in Java and Spring boot</title>
      <dc:creator>Chris Noring</dc:creator>
      <pubDate>Tue, 06 Dec 2022 21:49:02 +0000</pubDate>
      <link>https://dev.to/azure/how-you-can-build-a-web-api-in-java-and-spring-boot-56ga</link>
      <guid>https://dev.to/azure/how-you-can-build-a-web-api-in-java-and-spring-boot-56ga</guid>
      <description>&lt;h1&gt;
  
  
  Build a Web API in Java and Spring boot
&lt;/h1&gt;

&lt;p&gt;In this article you will learn to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configure your Visual Studio code environment for usage with Spring Boot.&lt;/li&gt;
&lt;li&gt;Create a Spring boot project.&lt;/li&gt;
&lt;li&gt;Learn how Spring enables you to create a REST API.&lt;/li&gt;
&lt;li&gt;Document your API via Swagger and Open API specification.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/docs/java/java-spring-boot" rel="noopener noreferrer"&gt;Spring boot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://azure.microsoft.com/en-us/resources/cloud-computing-dictionary/what-is-java-spring-boot?wt.mc_id=academic-0000-chnoring" rel="noopener noreferrer"&gt;What is Spring boot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/training/modules/deploy-java-spring-boot-app-service-mysql?wt.mc_id=academic-0000-chnoring" rel="noopener noreferrer"&gt;Deploy spring boot on Azure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;As this tutorial helps you create an API with an Open API spec file, you can take that and create a connector from it &lt;a href="https://learn.microsoft.com/en-us/connectors/custom-connectors/define-openapi-definition?wt.mc_id=academic-0000-chnoring" rel="noopener noreferrer"&gt;Create a custom connector from Open API&lt;/a&gt; a building block in Microsoft Power platform.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prereqs
&lt;/h2&gt;

&lt;p&gt;You should have the following installed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.java.com/en/download/" rel="noopener noreferrer"&gt;Java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://maven.apache.org/install.html" rel="noopener noreferrer"&gt;Maven&lt;/a&gt;, a building tool for building projects and maintaining dependencies &lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/Download" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Configure Visual Studio via extensions
&lt;/h2&gt;

&lt;p&gt;To make it easier to work with development of Web APIs in Java, you can install a set of extensions. These extensions will help you with tooling support around auto completion but also stopping starting and debugging your web apps.&lt;/p&gt;

&lt;p&gt;You need these two extensions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="//vscode:extension/vscjava.vscode-java-pack"&gt;Extension pack for Java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="//vscode:extension/pivotal.vscode-boot-dev-pack"&gt;Extension pack for Spring Boot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;which will install the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Java development kit JDK&lt;/li&gt;
&lt;li&gt;Extension Pack for Java&lt;/li&gt;
&lt;li&gt;Spring Boot Extension Pack, specific tooling around using Spring Boot.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To learn more on developing with Java using Visual Studio Code, go to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://code.visualstudio.com/docs/java/java-spring-boot" rel="noopener noreferrer"&gt;https://code.visualstudio.com/docs/java/java-spring-boot&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Exercise: Create a Spring boot project
&lt;/h2&gt;

&lt;p&gt;Once you've installed the extensions needed you can use the command palette in Visual Studio Code to scaffold your Spring Boot project.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Bring up the command pallette (View -&amp;gt; Command Palette) and choose the following command:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Spring Initializr: Create a Maven Project
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Specify a Spring Boot version, select &lt;strong&gt;3.0&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Specify a project language &lt;strong&gt;Java&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Input group id for your project &lt;strong&gt;com.example&lt;/strong&gt;, it will scaffold up a directory structure for you as well as adding this id in the &lt;code&gt;pom.xml&lt;/code&gt; file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Input Artifact Id for your project &lt;strong&gt;Demo&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Specify packaging type &lt;strong&gt;Jar&lt;/strong&gt;, you can change this later to War if you want (if you want to run your app in application server like Tomcat for example, you want a War file but for now the app will run in a built-in server)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Specify Java version&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Search for dependencies, you want to add &lt;strong&gt;Spring Web&lt;/strong&gt; at minimum, but might want to add other dependencies in the future like JPA, Session management and more. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Your project so far
&lt;/h2&gt;

&lt;p&gt;This scaffolded a project for you. Let's go through the parts you need to know about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
  main/
    java/
      com/
        example/
          demo/
            DemoApplication.java
    resources/
      application.properties 
  test/          
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see above, you have structure consisting of application files, &lt;em&gt;DemoApplication.java&lt;/em&gt; under &lt;code&gt;demo/&lt;/code&gt;, configuration files under &lt;code&gt;resoruces/&lt;/code&gt; and tests under &lt;code&gt;test/&lt;/code&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Application files
&lt;/h3&gt;

&lt;p&gt;So far, you only have one application file &lt;em&gt;DemoApplication.java&lt;/em&gt; which looks like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.demo&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.boot.SpringApplication&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.boot.autoconfigure.SpringBootApplication&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@SpringBootApplication&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Demo2Application&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;SpringApplication&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Demo2Application&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file is the entry point to your application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuration
&lt;/h3&gt;

&lt;p&gt;You have a configuration file &lt;em&gt;application.properties&lt;/em&gt; that starting out is empty but will be used as you add things to your app in need of configuring for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what port to start up on.&lt;/li&gt;
&lt;li&gt;if you got self documenting docs like Swagger.&lt;/li&gt;
&lt;li&gt;credentials for a database, if you're using one.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What are routes?
&lt;/h2&gt;

&lt;p&gt;In a Web API there are logical areas of your application like customers, orders, products and so on. When you have the user use your app you can send the user to these logical areas by specifying routes, parts of the URL. Imagine that you have the following URL &lt;a href="http://contoso.com" rel="noopener noreferrer"&gt;http://contoso.com&lt;/a&gt; and you have three areas in your app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;customers&lt;/strong&gt;, this is where you handle customers, adding new ones and updating existing ones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;products&lt;/strong&gt;, these are products the customers can buy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;orders&lt;/strong&gt;, these are orders the customers can place.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;based on these areas, we can now create areas of our app addressing them like so:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/customers&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/products&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;orders&lt;/code&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;so when a user then navigates to for example &lt;code&gt;/customers&lt;/code&gt; they would type in the full address &lt;a href="http://www.contoso.com/customers" rel="noopener noreferrer"&gt;http://www.contoso.com/customers&lt;/a&gt; but the route part would be &lt;code&gt;/customers&lt;/code&gt; a sub part of the URL. &lt;/p&gt;

&lt;p&gt;We can now write code that responds to a route like the ones mentioned above. &lt;/p&gt;

&lt;h3&gt;
  
  
  Adding a route
&lt;/h3&gt;

&lt;p&gt;To add a route, you need to create a class like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloServiceController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@RequestMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/hello"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RequestMethod&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;GET&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getHello&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HttpStatus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;OK&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down what's going on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; The decorator &lt;code&gt;@RestController&lt;/code&gt; ensures the class can respond to web request.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@RequestMapping(value = "/hello", method = RequestMethod.GET)&lt;/code&gt; does two things, response to a specific route "/hello" and to a specific HTTP verb, GET, which means to read data from the server.&lt;/li&gt;
&lt;li&gt;The method &lt;code&gt;getHello()&lt;/code&gt; produces a response of type &lt;code&gt;ResponseEntity&amp;lt;Object&amp;gt;&lt;/code&gt; and the specific call we make, &lt;code&gt;return new ResponseEntity&amp;lt;&amp;gt;("hello", HttpStatus.OK)&lt;/code&gt; produces a text response with code 200, &lt;code&gt;HttpStatus.OK&lt;/code&gt; which means everything is ok.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we know how we can create code to respond to route requests, let's add code to handle &lt;code&gt;/products&lt;/code&gt; route in the below exercise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exercise: adding a route
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create a &lt;code&gt;product&lt;/code&gt; directory in the same place as your &lt;em&gt;DemoApplication.java&lt;/em&gt; file and create the following files:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Product.java
   ProductController.java
   ProductRepository.java
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Give &lt;em&gt;Product.java&lt;/em&gt; the following content:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;   &lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.demo.product&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; 
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; 
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; 
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the &lt;code&gt;Product&lt;/code&gt; class will serve as our model.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Give &lt;em&gt;ProductRepository.java&lt;/em&gt; the following content:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;   &lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.demo.product&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.ArrayList&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductRepository&lt;/span&gt; 
    &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

      &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ProductRepository&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; 
      &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tomato"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt; 
      &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getProducts&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; 
      &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ProductRepository&lt;/code&gt; will now have one entry of type &lt;code&gt;Product&lt;/code&gt;. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Give &lt;code&gt;ProductController.java&lt;/code&gt; the following content:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.demo.product&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.http.HttpStatus&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.http.ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.web.bind.annotation.RequestMapping&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.web.bind.annotation.RestController&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.ArrayList&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@RestController&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/**
         * @return
         */&lt;/span&gt;
        &lt;span class="c1"&gt;// TODO: query param, route param&lt;/span&gt;
        &lt;span class="nd"&gt;@RequestMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/products"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getProducts&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
           &lt;span class="nc"&gt;ProductRepository&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ProductRepository&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; 
           &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProducts&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;HttpStatus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;OK&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have all the code needed to respond to a web request to &lt;code&gt;/products&lt;/code&gt;. Next, let's run our app:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select "Spring Boot dashboard" icon on the left menu in Visual Studio Code, should look like a stop icon.&lt;/li&gt;
&lt;li&gt;Select the &lt;em&gt;play&lt;/em&gt; icon to start your app&lt;/li&gt;
&lt;li&gt;In your browser, navigate to "&lt;a href="http://localhost:8080/products" rel="noopener noreferrer"&gt;http://localhost:8080/products&lt;/a&gt;", you should see the following output:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   http://localhost:8080/products
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Document your API via Open API (Swagger)
&lt;/h2&gt;

&lt;p&gt;Next, we want to learn how to document our API. We can use Open API to do so (formerly known as Swagger). In our &lt;em&gt;pom.xml&lt;/em&gt; file, the file Maven uses to keep track of dependencies, we can add the following dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springdoc&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;springdoc-openapi-starter-webmvc-ui&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.0.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;this will ensure our Maven project knows how to add the capability that will give us Open API support.&lt;/p&gt;

&lt;p&gt;On the command line, you can now type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;mvn package
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which will fetch all dependencies (if not fetched already) and add package your project into a Jar file. &lt;/p&gt;

&lt;h3&gt;
  
  
  Open API
&lt;/h3&gt;

&lt;p&gt;This dependency will do two things for us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create an Open API specification, a JSON file that describes our API, that can be used to create a connector for Microsoft Power Platform for example or in other ways help describe how to work with our API.&lt;/li&gt;
&lt;li&gt;Create a a UI that we can interact with to test our interface.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Exercise: add Open API
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Add the following XML to the &lt;code&gt;dependencies&lt;/code&gt; section of your &lt;em&gt;pom.xml&lt;/em&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;   &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springdoc&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;springdoc-openapi-starter-webmvc-ui&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.0.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;mvn package&lt;/code&gt; in the command line&lt;/li&gt;
&lt;li&gt;In resources directory, locate &lt;em&gt;application.properties&lt;/em&gt; and add the following content:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   springdoc.api-docs.path=/docs
   springdoc.swagger-ui.path=/swagger.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;this will ensure we can find our Open API spec file at &lt;code&gt;/docs&lt;/code&gt; and our UI at &lt;em&gt;swagger.html&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Start the app again via the Spring boot dashboard icon in Visual Studio Code:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Navigate to "&lt;a href="http://localhost:8080/docs" rel="noopener noreferrer"&gt;http://localhost:8080/docs&lt;/a&gt;", you should see a JSON output similar to below, i.e our specification file:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"openapi"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"3.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"info"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"OpenAPI definition"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"v0"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="nl"&gt;"servers"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Generated server url"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"paths"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"/hello"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="s2"&gt;"hello-service-controller"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nl"&gt;"operationId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"getHello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"responses"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"200"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"*/*"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"schema"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;}}}}}&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"/products"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="s2"&gt;"product-controller"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;"operationId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"getProducts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"responses"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"200"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"*/*"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"schema"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"$ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"#/components/schemas/Product"&lt;/span&gt;&lt;span class="p"&gt;}}}}}}},&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"put"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="s2"&gt;"product-controller"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nl"&gt;"operationId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"getProducts_3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"responses"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"200"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"*/*"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"schema"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"$ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"#/components/schemas/Product"&lt;/span&gt;&lt;span class="p"&gt;}}}}}}},&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"post"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="s2"&gt;"product-controller"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nl"&gt;"operationId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"getProducts_2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"responses"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"200"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"*/*"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"schema"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"$ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"#/components/schemas/Product"&lt;/span&gt;&lt;span class="p"&gt;}}}}}}},&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"delete"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="s2"&gt;"product-controller"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nl"&gt;"operationId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"getProducts_5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"responses"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"200"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"*/*"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"schema"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"$ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"#/components/schemas/Product"&lt;/span&gt;&lt;span class="p"&gt;}}}}}}},&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="s2"&gt;"product-controller"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nl"&gt;"operationId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"getProducts_6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"responses"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"200"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"*/*"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"schema"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"$ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"#/components/schemas/Product"&lt;/span&gt;&lt;span class="p"&gt;}}}}}}},&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"head"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="s2"&gt;"product-controller"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nl"&gt;"operationId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"getProducts_1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"responses"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"200"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"*/*"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"schema"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"$ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"#/components/schemas/Product"&lt;/span&gt;&lt;span class="p"&gt;}}}}}}},&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"patch"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="s2"&gt;"product-controller"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nl"&gt;"operationId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"getProducts_4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"responses"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"200"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"*/*"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"schema"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"$ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"#/components/schemas/Product"&lt;/span&gt;&lt;span class="p"&gt;}}}}}}}}},&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"components"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"schemas"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"Product"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;}}}}}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Navigate to &lt;a href="http://localhost:8080/swagger.html" rel="noopener noreferrer"&gt;http://localhost:8080/swagger.html&lt;/a&gt; this will present you with a graphical view, where you can try out your API.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Congrats, you've managed to build a Web API support the route &lt;code&gt;/products&lt;/code&gt; but you also managed to document it using Open API. You are know well setup do extend it further and add data source to it with for example JPA (Java Persistence API), which we will cover in our next article.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>java</category>
      <category>spring</category>
    </item>
    <item>
      <title>Go from the beginning - arrays and slices</title>
      <dc:creator>Chris Noring</dc:creator>
      <pubDate>Tue, 27 Sep 2022 16:41:52 +0000</pubDate>
      <link>https://dev.to/azure/go-from-the-beginning-arrays-and-slices-4heg</link>
      <guid>https://dev.to/azure/go-from-the-beginning-arrays-and-slices-4heg</guid>
      <description>&lt;p&gt;An array is group of elements that are connected. You want to use an array when you have a group of something, like many orders, cars, rows in a file. The idea is to collect all that data in one structure. You also want to be able to iterate over it and possibly carry out an operation on it as a group.&lt;/p&gt;

&lt;h2&gt;
  
  
  Series
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Your first program, &lt;strong&gt;you are here&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/softchris/go-from-the-beginning-using-variables-113f"&gt;Variables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/softchris/go-from-the-beginning-applying-boolean-logic-3418"&gt;If and else&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/azure/go-from-the-beginning-converting-between-types-hkg"&gt;Conversions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/azure/go-from-the-beginning-working-with-loops-2n58"&gt;Loops&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/azure/go-from-the-beginning-reading-user-input-1bbh"&gt;User input&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/azure/go-from-the-beginning-functions-315e"&gt;Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/azure/go-from-the-beginning-handle-errors-1fg6"&gt;Error handling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Arrays, &lt;strong&gt;you are here&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/H0Yhe-IZNWs"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Declare an array
&lt;/h2&gt;

&lt;p&gt;To declare an array, you need to specify the following properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;capacity&lt;/strong&gt;, how many elements it holds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;type&lt;/strong&gt;, what type of elements it holds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;array content&lt;/strong&gt;, you can assign it elements at creation or do so later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It starts with the square brackets, &lt;code&gt;[]&lt;/code&gt;. Within the square brackets you set the capacity, how many elements it can hold.&lt;/p&gt;

&lt;p&gt;and here's a more real example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;cities&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"NY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"LA"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the preceding code, an array of strings is declared. It has capacity for 5 elements and two of the places are filled with "NY" and "LA". Note also because we set the capacity to 5 and the number of elements it's assigned is 2, there's 3 spaces free.&lt;/p&gt;

&lt;h3&gt;
  
  
  Capacity by inference
&lt;/h3&gt;

&lt;p&gt;You don't have to set capacity to an explicit number, you can set it to &lt;code&gt;...&lt;/code&gt;, in which case the capacity will be set to the number of elements you assign to it, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The preceding code has 4 elements, and that's also its capacity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessing elements
&lt;/h2&gt;

&lt;p&gt;The way to access an element is by using its index. Index is 0 based, meaning the first index is 0 and its last is the length -1.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;// 1&lt;/span&gt;
&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;// 4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Length and capacity
&lt;/h2&gt;

&lt;p&gt;Imagine we have the following array declared:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;cities&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"NY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"LA"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;length&lt;/strong&gt;. The length is defined as the number of elements in the array. You can use the &lt;code&gt;len()&lt;/code&gt; method to find this out:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;   &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cities&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;capacity&lt;/strong&gt;. The capacity is how many elements the array can hold. &lt;code&gt;cap()&lt;/code&gt; is the method you use to find the capacity:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;   &lt;span class="nb"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cities&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// 5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Slices
&lt;/h2&gt;

&lt;p&gt;A slice is a part of an array. A slice is created when the slice operator is being used. Here's the syntax for the slice operator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;s&lt;/code&gt;, the array&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;i&lt;/code&gt;, the first index of the array to take elements from&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;p&lt;/code&gt;, The variable p corresponds to the last element in the underlying array that can be used in the new slice. I.e cut right before this index.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;part&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;// 2,3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding elements
&lt;/h3&gt;

&lt;p&gt;A slice differs from an array, you can add items to it. The &lt;code&gt;append()&lt;/code&gt; method lets you add elements to it. The syntax for &lt;code&gt;append()&lt;/code&gt; is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's how you can append to a slice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// 1,2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Removing elements
&lt;/h3&gt;

&lt;p&gt;Remove an element by constructing a new slice.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;letters&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"A"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"B"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"D"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"E"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;remove&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="c"&gt;// remove index&lt;/span&gt;
&lt;span class="c"&gt;// 0 - remove index, remove +1 to end   &lt;/span&gt;
&lt;span class="n"&gt;letters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;letters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;letters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create a slice with make
&lt;/h3&gt;

&lt;p&gt;You can use the &lt;code&gt;make()&lt;/code&gt; method to create a slice. Here's how:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;slice&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// creates a slice with length 5 and capacity 5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can set these to different values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;slice2&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slice2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slice2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slice2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the slice has length 2 and capacity 5.&lt;/p&gt;

&lt;h3&gt;
  
  
  Copy elements
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;dest&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c"&gt;// copies slice {1,2} into dest&lt;/span&gt;
&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// [1 2 0 0 0]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In summary, you were taught how to use Arrays. Additionally, we covered working with slices. Slices allows you to work with an array like structure and change its size as you see fit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Learn more
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/learn/modules/go-data-types/2-slices"&gt;https://docs.microsoft.com/en-us/learn/modules/go-data-types/2-slices&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>programming</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How YOU can deploy your resume in 2 min using Azure Static Web apps</title>
      <dc:creator>Chris Noring</dc:creator>
      <pubDate>Tue, 20 Sep 2022 15:54:26 +0000</pubDate>
      <link>https://dev.to/azure/how-you-can-deploy-your-resume-in-2-min-using-azure-static-web-apps-2nmd</link>
      <guid>https://dev.to/azure/how-you-can-deploy-your-resume-in-2-min-using-azure-static-web-apps-2nmd</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;TLDR; In this post, you'll deploy your resume to the cloud using Visual Studio Code and Azure Static Web Apps.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/v4n0AU2xlOo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://azure.microsoft.com/en-us/free/students?wt.mc_id=academic-0000-chnoring"&gt;Azure for students&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurestaticwebapps"&gt;Azure Static Web apps extension&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/download"&gt;Visual Studio Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Creating your resume
&lt;/h2&gt;

&lt;p&gt;Writing a resume is an important activity in landing your first job as a student. It's also important later in life for those times you're looking to grow in your career.&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure Static Web apps
&lt;/h2&gt;

&lt;p&gt;Azure Static Web Apps is an Azure service that lets you deploy simpler static sites as well as web apps built in a SPA framework. It even lets you add an API to build a fullstack app.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In short, there's a great grow up story.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Tools for deployment and app lifecycle
&lt;/h3&gt;

&lt;p&gt;Azure Static Web apps lets you deploy using more than one way, you can use of any of the following approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://learn.microsoft.com/en-us/cli/azure/install-azure-cli?wt.mc_id=academic-0000-chnoring"&gt;Azure CLI&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurestaticwebapps"&gt;Azure Static Web apps extension&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="//portal.azure.com"&gt;Azure Portal&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Azure/static-web-apps-cli"&gt;Azure Static Web Apps CLI&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;Azure Static Web apps does more for you than just deployment. In fact, it handles the complete app dev lifecycle. The first time you deploy, it creates a CI/CD pipeline so that any future changes, you only need to focus on pushing commits to GitHub. The rest, to redeploy, is all taken care of by a .yml file created in a .github directory in you repo. High-level, here's what's going on:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploy the first time&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project deployed.&lt;/li&gt;
&lt;li&gt;.yml file created in repo creating a CI/CD pipeline.&lt;/li&gt;
&lt;li&gt;GitHub actions deploying everything using the .yml file above.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Future changes&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You push commits to your repo&lt;/li&gt;
&lt;li&gt;Everything is built and deployed using GH actions and your .yml file.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deploy your resume
&lt;/h2&gt;

&lt;p&gt;To deploy our resume, we need the following software installed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;To deploy our resume, we need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A GitHub account and we need to be logged in.&lt;/li&gt;
&lt;li&gt;Visual Studio Code installed.&lt;/li&gt;
&lt;li&gt;Azure Static Web Apps and VS Code extension installed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Deployment
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;In this deployment, you can definitely create your own resume and repo or create one from this &lt;a href="https://github.com/MicrosoftDocs/swa-template/generate"&gt;template&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you start with static files (not from a GH repo), you will get some questions below regarding Git.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Type (Command + P or Control P) to open the command palette.&lt;/li&gt;
&lt;li&gt;Type "Azure Static Web Apps: Create Static Web App" to start the deployment process.&lt;/li&gt;
&lt;li&gt;Select your subscription&lt;/li&gt;
&lt;li&gt;Select "Create" when asked to create a Git repo as well as GH repo (if you start with only static files, if you start from a repo, you won't get this question)&lt;/li&gt;
&lt;li&gt;Enter a commit message, you can select what it proposes, i.e "Initial commit"&lt;/li&gt;
&lt;li&gt;Enter name of static web app&lt;/li&gt;
&lt;li&gt;Enter name of GH repository (it will be created for you)&lt;/li&gt;
&lt;li&gt;Select a region&lt;/li&gt;
&lt;li&gt;Select "Custom" as preset&lt;/li&gt;
&lt;li&gt;Enter location of application &lt;strong&gt;/&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enter location of build output, clear this field.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From here, it should be deploying your project - Congrats, your resume is online&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;You learned how you can deploy your resume in minutes using Azure Static Web Apps and Visual Studio Code.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>tutorial</category>
      <category>webdev</category>
      <category>vscode</category>
    </item>
    <item>
      <title>How YOU can build your own test framework in Python using decorators</title>
      <dc:creator>Chris Noring</dc:creator>
      <pubDate>Wed, 31 Aug 2022 14:33:36 +0000</pubDate>
      <link>https://dev.to/azure/how-you-can-build-your-own-test-framework-in-python-using-decorators-2b00</link>
      <guid>https://dev.to/azure/how-you-can-build-your-own-test-framework-in-python-using-decorators-2b00</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;TLDR; this article will show how decorators work, some uses for decorators and a bigger example building the start of test framework. It's ~15 min long BUT, there's a very cool project at the end of it - learning how to build your own test framework :)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, decorators, what are those, and why would I use them? You might have seen something like this before in your Python code &lt;code&gt;@debug&lt;/code&gt; or &lt;code&gt;@log&lt;/code&gt; right above your function signature like so:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@log&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Running your method, you might have gotten out some useful information about the &lt;code&gt;method()&lt;/code&gt;. If you haven't seen this, that's fine, let me take you on a journey.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/learn/paths/python-language?wt.mc_id=academic-0000-chnoring" rel="noopener noreferrer"&gt;Python build real world apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/learn/paths/introduction-python-space-exploration-nasa?wt.mc_id=academic-0000-chnoring" rel="noopener noreferrer"&gt;Python space exploration NASA&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/learn/paths/beginner-python?wt.mc_id=academic-0000-chnoring" rel="noopener noreferrer"&gt;Python for beginners&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Decorators
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;@&lt;/code&gt; character denotes something we call decorators; the idea is to &lt;em&gt;decorate&lt;/em&gt; a function without the function being aware. It can add capabilities to the function like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;execution time&lt;/strong&gt;, how long it takes the function to run from start to finish.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;debugging&lt;/strong&gt;, information on function name, parameters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's look at a case where we &lt;em&gt;decorate&lt;/em&gt; a function:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BEFORE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AFTER&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[LOG]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Running this function results in the following output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

BEFORE
[LOG]
AFTER


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Python decorators
&lt;/h3&gt;

&lt;p&gt;Python has a specific way of handling decorators, so much so it lets you use a shorthand &lt;code&gt;@&lt;/code&gt;. But first, let's tweak the above code so that it's a good candidate to using the shorthand. Change the code to the following:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BEFORE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AFTER&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[LOG]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;What we did was to create an inner function &lt;code&gt;wrapper()&lt;/code&gt; in &lt;code&gt;decorator()&lt;/code&gt; function and return it. Also, when invoking &lt;code&gt;decorator()&lt;/code&gt;, we save the response to &lt;code&gt;fn&lt;/code&gt; and invoke that:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="n"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here's the beautiful part, Python lets us turn the above code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[LOG]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and turn it into the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@decorator&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[LOG]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note above how &lt;code&gt;@decorator&lt;/code&gt; have replaced the call to &lt;code&gt;decorator()&lt;/code&gt; and &lt;code&gt;@decorator&lt;/code&gt; is now above the definition of &lt;code&gt;log()&lt;/code&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pause here for a moment and take this in. This is a great start!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Parameters
&lt;/h2&gt;

&lt;p&gt;OK, so great progress so far, we can write decorators, but what happens if we want to use parameters on functions being decorated? Well, let's see what happens:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@decorator&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;lhs&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt;

&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You get an error:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

TypeError: wrapper() takes 0 positional arguments but 2 were given


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;So, we're not handling incoming parameters, how to fix?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, when you call a function, there are two parameters that Python works with under the hood:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;*args&lt;/code&gt;, a list of parameter values.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;**kwargs&lt;/code&gt;, a list of tuples where each tuple is the name and value of your parameter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can fix the above issue if you send these via your wrapper function into the function being decorated like so:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Running the code again, it compiles this time, but the output says &lt;code&gt;None&lt;/code&gt;, so there seems to be some other problem, where we lose the return value of our decorated function.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How can we solve that?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What we need to do is to ensure the &lt;code&gt;wrapper()&lt;/code&gt; function captures the return value from the function being decorated and return that in turn, like so:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Great, so we learned a little more about dealing with parameters and return values with decorators. Let's learn next how we can find out more about the function we decorate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Instrumentation
&lt;/h2&gt;

&lt;p&gt;We're building up our knowledge steadily so we're able to use it to build a bigger project at the end of the article. Now let's look at how we can find out more about the function we decorate. In Python, you can inspect and find out more about a function by using the &lt;code&gt;help()&lt;/code&gt; method. If you only want to find out a functions' name, you can refer to &lt;code&gt;__name__&lt;/code&gt; property like so:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[LOG]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# prints log
&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;How does this work in a decorator, is there a difference?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's try it by adding it to the decorator:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;calling function: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt;

&lt;span class="nd"&gt;@decorator&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;lhs&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Calling the above code, we get the following output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

wrapper
calling function:  add
2


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It seems, &lt;code&gt;add()&lt;/code&gt; within the decorator is rightly referred to as &lt;strong&gt;add&lt;/strong&gt;. However, outside of the decorator calling &lt;code&gt;add.__name__&lt;/code&gt;, it goes by &lt;strong&gt;wrapper&lt;/strong&gt;, it seems confused, no?&lt;/p&gt;

&lt;p&gt;We can fix this using a library called &lt;code&gt;functools&lt;/code&gt; that will preserve the function's information like so:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@functools.wraps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and our full code now looks like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;functools&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="nd"&gt;@functools.wraps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;calling function: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt;

&lt;span class="nd"&gt;@decorator&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;lhs&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Running this code, we see that it's no longer confused and the &lt;code&gt;add()&lt;/code&gt; function is referred to as &lt;strong&gt;add&lt;/strong&gt; in both places.&lt;/p&gt;

&lt;p&gt;Ok, we know enough about decorators at this point to be dangerous, let's build something fun, a test framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assignment - build a test framework
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;DISCLAIMER: I should say there are many test frameworks out there for Python. This is an attempt to put you decoration skills into work, not to add to an existing pile of excellent frameworks, however, you're free to hack away at this project if you find it useful :)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  -1- How should it work
&lt;/h3&gt;

&lt;p&gt;Ok, let's start with how it should work, that will drive the architecture and the design.&lt;/p&gt;

&lt;p&gt;We want to have the following experience as developers writing tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I just want to add a &lt;code&gt;@test&lt;/code&gt; decorator.&lt;/li&gt;
&lt;li&gt;I want to call some type of assertion in my test methods.&lt;/li&gt;
&lt;li&gt;I want to do max one function call to run all tests.&lt;/li&gt;
&lt;li&gt;I want to see tests that succeed and fail, and I want extra info about the failing test so I can correct it&lt;/li&gt;
&lt;li&gt;Nice to have, please add color to the terminal output.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Based on the above, my test file should look something like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@test&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;testAddShouldSucceed&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="n"&gt;lhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="n"&gt;rhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{lhs} + {rhs} should be equal to {sum}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="nd"&gt;@test&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;testAddShouldSucceed2&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="n"&gt;lhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="n"&gt;rhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{lhs} + {rhs} should be equal to {sum}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  -2- Building the parts, &lt;code&gt;decorator()&lt;/code&gt;, &lt;code&gt;expect()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Ok, based on the previous part, we need three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@test&lt;/code&gt;, a decorator.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;expect()&lt;/code&gt; a method that checks if two values are equal.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;run()&lt;/code&gt; a method running all functions decorated with &lt;code&gt;@test&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's get to work. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a file &lt;em&gt;decorator.py&lt;/em&gt; and give it the following content:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="nd"&gt;@functools.wraps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;. PASS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;TestError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;te&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; FAILED with message: &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;te&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mess&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;#&amp;nbsp;fns.append(wrapper)
&lt;/span&gt;      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The above decorator &lt;code&gt;test()&lt;/code&gt; will run test method and print &lt;strong&gt;. PASS&lt;/strong&gt; if the method runs without error. However, if &lt;code&gt;TestError&lt;/code&gt; is thrown, you will see &lt;strong&gt; FAILED with message &lt;/strong&gt;. How can a test method crash? Let's implement &lt;code&gt;expect()&lt;/code&gt; next.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a file &lt;em&gt;expect.py&lt;/em&gt; and give it the following code:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

   &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;util&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestError&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mess&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
     &lt;span class="nf"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lhs&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
     &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;TestError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Ok, so return true if all good, and crash if not, got it!. What is &lt;code&gt;TestError&lt;/code&gt;?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a file &lt;em&gt;util.py&lt;/em&gt; and give it the following code:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

   &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
     &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mess&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
       &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mess&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Above we're inheriting from &lt;code&gt;Exception&lt;/code&gt; and we'e adding a field &lt;code&gt;mess&lt;/code&gt; that we store any error message in.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Let's revisit &lt;em&gt;decorator.py&lt;/em&gt;. Look at the following codeline:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
      &lt;span class="nd"&gt;@functools.wraps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;. PASS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;TestError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;te&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; FAILED with message: &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;te&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mess&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# fns.append(wrapper)
&lt;/span&gt;      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Enable the commented out line &lt;code&gt;# fns.append(wrapper)&lt;/code&gt;. What this line does is to add each test function to a list. We can use that when we implement the &lt;code&gt;run()&lt;/code&gt; function. Add &lt;code&gt;run()&lt;/code&gt; to the bottom of the file with this code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
     &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;fns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Great, now we just have one thing missing, some colors :)  &lt;/p&gt;

&lt;h3&gt;
  
  
  -3- Give it color
&lt;/h3&gt;

&lt;p&gt;To give it colors let's install a library &lt;strong&gt;coloroma&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a virtual environment&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

   python3 -m venv test-env


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Activate the virtual environment&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

   source test-env/bin/activate


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Install &lt;strong&gt;colorama&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

   pip install colorama


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Adjust &lt;em&gt;decorator.py&lt;/em&gt; and ensure the code looks like so:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

   &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;functools&lt;/span&gt;

   &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;colorama&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Fore&lt;/span&gt;
   &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;colorama&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Style&lt;/span&gt;
   &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;util&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestError&lt;/span&gt;

   &lt;span class="n"&gt;fns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
     &lt;span class="nd"&gt;@functools.wraps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
         &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
           &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
           &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Fore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GREEN&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. PASS&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RESET_ALL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;TestError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;te&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
           &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Fore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RED&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; FAILED with message: &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;te&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mess&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RESET_ALL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="n"&gt;fns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
      &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;fns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Create a file &lt;em&gt;demo.py&lt;/em&gt; and give it the following content:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

   &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;decorator&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;
   &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;expect&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;expect&lt;/span&gt;

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;lhs&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt;

   &lt;span class="nd"&gt;@test&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;testAddShouldSucceed&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
     &lt;span class="n"&gt;lhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
     &lt;span class="n"&gt;rhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
     &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
     &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{lhs} + {rhs} should be equal to {sum}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

   &lt;span class="nd"&gt;@test&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;testAddShouldSucceed2&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
     &lt;span class="n"&gt;lhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
     &lt;span class="n"&gt;rhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
     &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
     &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{lhs} + {rhs} should be equal to {sum}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

   &lt;span class="nd"&gt;@test&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;testAddShouldFail&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
     &lt;span class="n"&gt;lhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
     &lt;span class="n"&gt;rhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
     &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
     &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{lhs} + {rhs} should be equal to {sum}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rhs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**main**&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  -4- Demo time
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Run the program&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

   python demo.py


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F643dtissqw5alxbfexgt.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F643dtissqw5alxbfexgt.png" alt="colored output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Congrats, you've built your first test framework.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Ideas for improvement
&lt;/h3&gt;

&lt;p&gt;So, what's next?&lt;/p&gt;

&lt;p&gt;Well, the &lt;code&gt;expect()&lt;/code&gt; method is limited in what it can compare, see if you can build other functions capable of comparing lists, objects and so on. Also, how would you test if a method throws a specific exception?&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;You've learned about decorators and their use cases. You've also built something useful with them - a test framework. Try it out, change it to your liking. Also, I would recommend reading this excellent post as well, &lt;a href="https://realpython.com/primer-on-python-decorators/" rel="noopener noreferrer"&gt;https://realpython.com/primer-on-python-decorators/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>tutorial</category>
      <category>programming</category>
      <category>testing</category>
    </item>
    <item>
      <title>How YOU can build your first Low code app using Microsoft Power Platform</title>
      <dc:creator>Chris Noring</dc:creator>
      <pubDate>Mon, 08 Aug 2022 13:34:32 +0000</pubDate>
      <link>https://dev.to/azure/how-you-can-build-your-first-low-code-app-using-microsoft-power-platform-bg5</link>
      <guid>https://dev.to/azure/how-you-can-build-your-first-low-code-app-using-microsoft-power-platform-bg5</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;TLDR; in this article, you will learn how to build your first Low Code application using Power platform.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You will learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lay out controls for displaying and capturing data.&lt;/li&gt;
&lt;li&gt;Respond to events.&lt;/li&gt;
&lt;li&gt;Save state in a variable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/PvvZ9H4u38Q"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/power-apps/maker/canvas-apps/getting-started?wt.mc_id=academic-0000-chnoring" rel="noopener noreferrer"&gt;Canvas apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/power-apps/maker/canvas-apps/get-started-test-drive?wt.mc_id=academic-0000-chnoring" rel="noopener noreferrer"&gt;Canvas app tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/power-apps/maker/canvas-apps/reference-properties" rel="noopener noreferrer"&gt;Control reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtu.be/PvvZ9H4u38Q" rel="noopener noreferrer"&gt;Video URL&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Low code and why do we care?
&lt;/h2&gt;

&lt;p&gt;Low code development is using blocks instead of specific code to build apps and services. There are many Low Code platforms, Microsoft's is called Microsoft Power Platform.&lt;/p&gt;

&lt;p&gt;The reason these platforms exist is that there's a great need to build business apps and not enough developers to build them. Also, many times non developer sit on business expertise, and it takes time to convey all they know to developers.&lt;/p&gt;

&lt;p&gt;With a low code platform, you can empower these business experts and turn them into citizen developers, and have them build apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Canvas app
&lt;/h2&gt;

&lt;p&gt;There are many types of low code apps you can build with Microsoft Power Platform. Canvas apps lets you build traditional apps in many ways. &lt;/p&gt;

&lt;p&gt;You can either build everything from scratch or model driven from a data source like for example Dataverse.&lt;/p&gt;

&lt;p&gt;The idea is that you design an app much like a Powerpoint slide and use Excel like expressions to add logic. &lt;/p&gt;

&lt;h2&gt;
  
  
  DEMO - build your first app
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;a href="https://make.powerapps.com" rel="noopener noreferrer"&gt;https://make.powerapps.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Select "+ Create", to create a canvas app.&lt;/li&gt;
&lt;li&gt;Select "Blank app", in the main area on your right.&lt;/li&gt;
&lt;li&gt;Give it an app name and select &lt;strong&gt;Tablet&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;+&lt;/strong&gt; in the left menu to add controls.&lt;/li&gt;
&lt;li&gt;Select a &lt;strong&gt;Text label&lt;/strong&gt; and drag it onto the screen.&lt;/li&gt;
&lt;li&gt;Select the control and change the Text property, either in the control or via the dropdown like so:&lt;/li&gt;
&lt;/ol&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6j8v28lb87tjiinsc2i6.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6j8v28lb87tjiinsc2i6.png" alt="Change label value"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select a button and drag it onto the screen. Change its value to "Save". &lt;/li&gt;
&lt;li&gt;Select the button and ensure the &lt;code&gt;OnSelect&lt;/code&gt; property is selected.&lt;/li&gt;
&lt;li&gt;Change the fx value to &lt;code&gt;UpdateContext({ myvar: "clicked" })&lt;/code&gt;, this will create a variable &lt;code&gt;myvar&lt;/code&gt; and give it the value "clicked":&lt;/li&gt;
&lt;/ol&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpmsh1670cst4iir1usyx.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpmsh1670cst4iir1usyx.png" alt="OnSelect, create variable"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select your label and change its content, in the fx area, to the following &lt;code&gt;"Show text:" &amp;amp; myvar&lt;/code&gt;, this will ensure it shows the content of a static text and the value of &lt;code&gt;myvar&lt;/code&gt;. &lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;&amp;amp;&lt;/code&gt; concatenates two strings together.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Test your program by clicking the play icon on the top right side:&lt;/li&gt;
&lt;/ol&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vnkmmxn3tb87z6oyceg.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vnkmmxn3tb87z6oyceg.png" alt="Select preview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Try clicking the button and ensure it says "clicked".
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Nicely done, you've created your first Canvas app, but we can improve it a little by relying on text inputs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use a text field
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Drag a Text input out onto the main surface. &lt;/li&gt;
&lt;li&gt;Note the name of the Text Input, you will refer to it now in the &lt;code&gt;OnSelect&lt;/code&gt; property of the button:&lt;/li&gt;
&lt;/ol&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Felgux8znxecgm6hn2w14.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Felgux8znxecgm6hn2w14.png" alt="Text input name"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Change the &lt;code&gt;OnSelect&lt;/code&gt; fx value of the button to the following code
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   UpdateContext({ myvar: TextInput2.Text })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code will set &lt;code&gt;myvar&lt;/code&gt; to the content of the TextInput2 control and its Text property.  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run your program: 

&lt;ol&gt;
&lt;li&gt;Enter some text, for example "abc" in the text field&lt;/li&gt;
&lt;li&gt;Click the button, you should now see the label be updated accordingly to "Show text: abc", it's working.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;Let's improve our program next by clearing the input field after we click a button.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clear input fields
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Select the button and add the following code in the fx area:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Reset(TextInput2)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your full code in &lt;code&gt;OnSelect&lt;/code&gt; should look like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   UpdateContext({ myvar: TextInput2.Text });

   Reset(TextInput2);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Drag the fx field down vertically to see more of it. Note how we added semicolons, &lt;code&gt;;&lt;/code&gt; above to separate between multiple statements.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;this code will clear the text input field and reset it to its default value.&lt;/p&gt;

&lt;p&gt;You were show one way so far to clear a text input, there's another way, lets do that next.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select the text input field and find a property in the control detail on the right called "Clear button" and enable it, like so:&lt;/li&gt;
&lt;/ol&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd21h0grx6rfqmak50lqv.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd21h0grx6rfqmak50lqv.png" alt="clear button"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run the program and notice the "X" that appears in the text field. This "X" will let you clear the text field via the UI.&lt;/li&gt;
&lt;/ol&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylp3rgaveoeybdy4xp7p.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fylp3rgaveoeybdy4xp7p.png" alt="clear button UI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations, you've taken your first steps to using controls and understand how they work.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>tutorial</category>
      <category>powerapps</category>
      <category>lowcode</category>
    </item>
    <item>
      <title>Interview problem - FizzBuzz</title>
      <dc:creator>Chris Noring</dc:creator>
      <pubDate>Wed, 06 Jul 2022 21:35:00 +0000</pubDate>
      <link>https://dev.to/azure/interview-problem-fizzbuzz-525c</link>
      <guid>https://dev.to/azure/interview-problem-fizzbuzz-525c</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/O_qd_TQAKCQ"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;So, you try to pass an interview as a programmer. There are a few ways you're tested, it's not just coding skills but your ability to communicate.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Communicate, what do you mean, code either compiles and works or it don't?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, no, so you live in a world with stakeholder and team members. In that world, you need to navigate and that means understanding a problem, asking clarifying questions and be prepared for changes and yes sometimes short deadlines.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;So what can I expect on the interview then?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, for one, a problem, and not just a problem, you're expected to communicate from start to finish, through all the phases.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What phases?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Understand the problem
&lt;/h2&gt;

&lt;p&gt;In this phase, you need to listen, repeat back what you heard and from there communicate assumptions. If those assumptions are incorrect, you may get clarifications. What you don't want to do is to just code away thinking you understood everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem breakdown
&lt;/h2&gt;

&lt;p&gt;In this phase, you should start to think about how to break down a problem in easier parts, sub problems if you will. At the job, you will be given big and complicated tasks and you will be asked to break it down into smaller parts that you can solve. - That's the job, oh and coding :).&lt;/p&gt;

&lt;h2&gt;
  
  
  Start coding
&lt;/h2&gt;

&lt;p&gt;At this point, start implementing the easier parts of your problem, a naive solution if you will. See if your interviewer has any comments. Then proceed until you have a working solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cover all your bases
&lt;/h2&gt;

&lt;p&gt;At this point, ensure you communicate any edge cases that you might need to handle and possible improvements to your code.&lt;/p&gt;

&lt;p&gt;Here it's likely your interviewer may spot a bug or ask you to implement a new requirement. Don't panic, take your time and figure out how to solve. &lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In the last part, the interviewer may sum up the interview and ask general questions, but if you have communicated thus far, you've written reasonable code that mostly works, chances are you pass this interview. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;So what problem am I likely to get, can you give m an example?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The problem : Fizz Buzz
&lt;/h2&gt;

&lt;p&gt;Lets take a problem called Fizz Buzz. Here's the description:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your code is divisible by 5 and 3. If both then print "FizzBuzz", if only by 3, then "Fizz" if only by 5, then "Buzz". &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To approach this problem, ensure you understand its sub problems:&lt;/p&gt;

&lt;p&gt;1) Divisible, that a number is divided and leaves no remainder and you at least in Python have a module operator to help you.&lt;br&gt;
2) Understand what condition to implement first, i.e. you should start with divisible by 5 and 3 and then add the other conditions.&lt;/p&gt;

&lt;p&gt;Here's a solution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;no&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;no&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; FizzBuzz"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;no&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: Buzz"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;no&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; Fizz"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;no&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and yea, take your time implementing and COMMUNICATE cause you're tested on both skills.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>python</category>
      <category>student</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
