[{"data":1,"prerenderedAt":717},["ShallowReactive",2],{"articles-automate-api-governance":3},{"id":4,"title":5,"body":6,"date":702,"description":703,"extension":704,"image":705,"imageAuthor":706,"imageLicense":706,"imageSource":706,"meta":707,"navigation":708,"path":709,"published":708,"seo":710,"slug":711,"stem":712,"tags":713,"__hash__":716},"articles\u002Farticles\u002Fautomate-api-governance.md","Automate Your API Governance In 15 Minutes",{"type":7,"value":8,"toc":695},"minimark",[9,21,27,30,35,38,41,55,59,76,79,82,85,89,92,95,98,117,120,123,134,137,140,150,153,162,166,171,176,179,182,210,217,222,230,237,243,250,405,412,422,428,431,434,437,462,467,473,476,479,643,646,649,655,658,661,664,668,671,674,677,680,683,686,691],[10,11,12,13,20],"p",{},"There’s a quote from Arnaud Lauret — author of The Design of Web APIs — during his ",[14,15,19],"a",{"href":16,"rel":17},"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=EMLCNqx80W4&t=1s&pp=ygUSbm9yZGljIGFwaXMgYXJuYXVk",[18],"nofollow","talk on API Governance at Nordic APIs"," that has stuck with me for a while now:",[22,23,24],"blockquote",{},[10,25,26],{},"“Ideally governance enables developers to be as autonomous as possible and as expert as needed.”",[10,28,29],{},"In this article I demonstrate one of the many ways you can improve consistency in applying governance policies by writing a rule to apply on your API description in order to make developers more autonomous.",[31,32,34],"h2",{"id":33},"api-governance-execution-is-hard","API Governance Execution Is Hard",[10,36,37],{},"Many organisations use governance to enforce rules onto their developers. Resulting in a plethora of authorities, review committees and additional processes to ensure policies are developed, maintained and applied.",[10,39,40],{},"Many meetings are held, and decisions tend to be made, documented and then partially or fully forgotten by the time teams come around to implement them.",[10,42,43,44,49,50,54],{},"According to ",[14,45,48],{"href":46,"rel":47},"https:\u002F\u002Fwww.postman.com\u002Fstate-of-api\u002F2024\u002F#:~:text=74%25%20of%20respondents%20are%20API,up%20from%2066%25%20in%202023.&text=APIs%20are%20no%20longer%20an,and%20boosting%20efficiency%20across%20teams.",[18],"the Postman State of API 2024 report","; ",[51,52,53],"em",{},"58% of developers rely on internal documentation, but 39% say inconsistent docs are the biggest roadblock."," Which in part will be attributed to documents that have been created as a way capture agreements and rules on API development.",[31,56,58],{"id":57},"beware-a-dangerously-large-number-of-apis-ahead","Beware! A Dangerously Large Number of APIs Ahead",[10,60,61,62,70,71],{},"Whilst API governance remains an area of improvement for many, the number of APIs is on the same steady rise they’ve been on for the last years. Organisations are ",[51,63,64,65,69],{},"“juggling an average of 421 APIs ",[66,67,68],"span",{},"…"," expected to grow by at least 10% in the next two years alone”"," according to ",[14,72,75],{"href":73,"rel":74},"https:\u002F\u002Fwww.f5.com\u002Fresources\u002Freports\u002Fstate-of-application-strategy-report",[18],"F5’s State of Application Strategy report.",[10,77,78],{},"As APIs can offer tremendous value to an organisation. This means that you’ll be looking for ways to maximize this API-enable value whilst eliminating any risks on a sprawl of technical debt and inconsistent implementations.",[10,80,81],{},"The mandate from management — sometimes hardly obtained — to do “something with API strategy and governance” give you the green light to change something. But actually “doing the something” is the hardest part.",[10,83,84],{},"Let’s look at different types of decisions, how they tie into governance and what we can do with some example policies.",[31,86,88],{"id":87},"design-decisions-vs-implementation-decisions","Design Decisions vs. Implementation Decisions",[10,90,91],{},"Chances are likely you are dealing with more than one developer. We know for a fact that not every policy will be considered evenly important by everyone.",[10,93,94],{},"Therefore, you want to automate policies that are easily supported through existing tooling.",[10,96,97],{},"Example of decisions you do want to automate:",[99,100,101,105,108,111,114],"ul",{},[102,103,104],"li",{},"How do I formulate a description?",[102,106,107],{},"What casing do we use throughout the organisation?",[102,109,110],{},"What responses do I add to an API description?",[102,112,113],{},"How do I structure an API description?",[102,115,116],{},"What features should I use from a specification standard?",[10,118,119],{},"You want to automate these decisions to keep developers going in a steady pace without having to resort to using random placeholder values, skip on easily addable information or be forced to contact an integration\u002FAPI team.",[10,121,122],{},"Example of decisions you don’t want to automate:",[99,124,125,128,131],{},[102,126,127],{},"What operations do I support with my API?",[102,129,130],{},"What parameters do I support with my API?",[102,132,133],{},"What endpoints do I expose?",[10,135,136],{},"Decisions are clearly distinguished into design-related and implementation-related. Both types of decisions need to be made to develop successful APIs though the implementation-related decisions are easier and safer to automate.",[10,138,139],{},"Validating an implementation according to a standard or a specification is a rather binary decision to make:",[10,141,142],{},[51,143,144,145,149],{},"Naming must be in CamelCase: ",[146,147,148],"code",{},"home-address"," is not in CamelCase.",[10,151,152],{},"Whilst validating a design is a combination of concepts and evaluations that rather appear on a spectrum.",[10,154,155],{},[51,156,157,158,161],{},"API must be secured: ",[146,159,160],{},"OAuth implemented",". This only contributes partly to a secure API design.",[31,163,165],{"id":164},"lets-setup-some-automated-rules","Let’s Setup Some Automated Rules",[10,167,168],{},[51,169,170],{},"Note: Throughout this example we’ll be using the Pet Store 3.1.0 specification example.",[10,172,173],{},[51,174,175],{},"Usually, I’m not too keen on using this example since it’s hardly representative of API descriptions you’ll encounter in real-life projects. Since we’re looking at implementing tooling to an API description and not the description itself, we’re fine using this.",[10,177,178],{},"We’ll be using npm and Spectral to create two custom rules we’ll apply to the Pet Store specification.",[10,180,181],{},"Assuming you haven’t worked with Spectral before we’ll start with a global install so we can use the CLI on our local machine.",[183,184,189],"pre",{"className":185,"code":186,"language":187,"meta":188,"style":188},"language-bash shiki shiki-themes github-dark-default","npm i -g @stoplight\u002Fspectral-cli\n","bash","",[146,190,191],{"__ignoreMap":188},[66,192,195,199,203,207],{"class":193,"line":194},"line",1,[66,196,198],{"class":197},"sQhOw","npm",[66,200,202],{"class":201},"s9uIt"," i",[66,204,206],{"class":205},"sFSAA"," -g",[66,208,209],{"class":201}," @stoplight\u002Fspectral-cli\n",[10,211,212],{},[213,214],"img",{"alt":215,"src":216},"Installing Spectral CLI globally","\u002Farticles\u002Fautomate-api-governance\u002Fspectral-1.jpg",[10,218,219],{},[51,220,221],{},"Reference spectral documentation for CICD integration or other setups.",[10,223,224,225,229],{},"After installing we can start writing a custom rule. In our working directory we only have our ",[226,227,228],"strong",{},"petstore.openapi.yml"," API specification.",[10,231,232,233,236],{},"Running the ",[146,234,235],{},"spectral lint"," command will clarify we need to provide a ruleset to apply.",[10,238,239],{},[213,240],{"alt":241,"src":242},"Running the lint command","\u002Farticles\u002Fautomate-api-governance\u002Fspectral-2.jpg",[10,244,245,246,249],{},"Let’s create a custom ruleset called ",[226,247,248],{},"object-policies.spectral.json"," in which we’ll define our custom rule.",[183,251,255],{"className":252,"code":253,"language":254,"meta":188,"style":188},"language-json shiki shiki-themes github-dark-default","{\n  \"description\": \"Object policies\",\n  \"rules\": {\n    \"object-policies:required-description\": {\n      \"description\": \"Object must have a description\",\n      \"message\": \"Object must have a description\",\n      \"given\": [\"$..schemas[*]\"],\n      \"severity\": \"error\",\n      \"then\": {\n        \"field\": \"description\",\n        \"function\": \"truthy\"\n      }\n    }\n  }\n}\n","json",[146,256,257,263,279,288,296,309,321,336,349,357,370,381,387,393,399],{"__ignoreMap":188},[66,258,259],{"class":193,"line":194},[66,260,262],{"class":261},"sZEs4","{\n",[66,264,266,270,273,276],{"class":193,"line":265},2,[66,267,269],{"class":268},"sPWt5","  \"description\"",[66,271,272],{"class":261},": ",[66,274,275],{"class":201},"\"Object policies\"",[66,277,278],{"class":261},",\n",[66,280,282,285],{"class":193,"line":281},3,[66,283,284],{"class":268},"  \"rules\"",[66,286,287],{"class":261},": {\n",[66,289,291,294],{"class":193,"line":290},4,[66,292,293],{"class":268},"    \"object-policies:required-description\"",[66,295,287],{"class":261},[66,297,299,302,304,307],{"class":193,"line":298},5,[66,300,301],{"class":268},"      \"description\"",[66,303,272],{"class":261},[66,305,306],{"class":201},"\"Object must have a description\"",[66,308,278],{"class":261},[66,310,312,315,317,319],{"class":193,"line":311},6,[66,313,314],{"class":268},"      \"message\"",[66,316,272],{"class":261},[66,318,306],{"class":201},[66,320,278],{"class":261},[66,322,324,327,330,333],{"class":193,"line":323},7,[66,325,326],{"class":268},"      \"given\"",[66,328,329],{"class":261},": [",[66,331,332],{"class":201},"\"$..schemas[*]\"",[66,334,335],{"class":261},"],\n",[66,337,339,342,344,347],{"class":193,"line":338},8,[66,340,341],{"class":268},"      \"severity\"",[66,343,272],{"class":261},[66,345,346],{"class":201},"\"error\"",[66,348,278],{"class":261},[66,350,352,355],{"class":193,"line":351},9,[66,353,354],{"class":268},"      \"then\"",[66,356,287],{"class":261},[66,358,360,363,365,368],{"class":193,"line":359},10,[66,361,362],{"class":268},"        \"field\"",[66,364,272],{"class":261},[66,366,367],{"class":201},"\"description\"",[66,369,278],{"class":261},[66,371,373,376,378],{"class":193,"line":372},11,[66,374,375],{"class":268},"        \"function\"",[66,377,272],{"class":261},[66,379,380],{"class":201},"\"truthy\"\n",[66,382,384],{"class":193,"line":383},12,[66,385,386],{"class":261},"      }\n",[66,388,390],{"class":193,"line":389},13,[66,391,392],{"class":261},"    }\n",[66,394,396],{"class":193,"line":395},14,[66,397,398],{"class":261},"  }\n",[66,400,402],{"class":193,"line":401},15,[66,403,404],{"class":261},"}\n",[10,406,407,408,411],{},"The goal of this rule is to enforce developers to include a ",[146,409,410],{},"description"," property for all schemas they define.",[10,413,414,417,418,421],{},[226,415,416],{},"Important!"," Take into consideration the intent of the policy within the development process. ",[226,419,420],{},"Why"," should a developer be enforced to add a property as this is a seemingly added technical constraint.",[10,423,424,427],{},[226,425,426],{},"Our rationale here is to enforce a description to be present because we want to make sure a functional description is added."," Because we want to ensure our API descriptions will be easily understood by external developers.",[10,429,430],{},"Part of writing the rule is to determine the severity. This can strongly impact the development process depending on how and where the linting is done.",[10,432,433],{},"Decide together with all related teams what is important enough to halt the pipeline and what should be considered a warning or hint.",[10,435,436],{},"To apply the rule, we’ve written to the specification we execute",[183,438,440],{"className":185,"code":439,"language":187,"meta":188,"style":188},"$ spectral lint petstore.openapi.yml --ruleset object-policies.spectral.json\n",[146,441,442],{"__ignoreMap":188},[66,443,444,447,450,453,456,459],{"class":193,"line":194},[66,445,446],{"class":197},"$",[66,448,449],{"class":201}," spectral",[66,451,452],{"class":201}," lint",[66,454,455],{"class":201}," petstore.openapi.yml",[66,457,458],{"class":205}," --ruleset",[66,460,461],{"class":201}," object-policies.spectral.json\n",[10,463,464],{},[51,465,466],{},"For this example, I’ve decided to create a dedicated file to contain all policies related to object validation. When you simply create a file called “.spectral.json”, Spectral will automatically apply these rule without the explicit “ — ruleset” flag.",[10,468,469],{},[213,470],{"alt":471,"src":472},"Running the lint command with ruleset","\u002Farticles\u002Fautomate-api-governance\u002Fspectral-3.jpg",[10,474,475],{},"Let’s add an additional rule. Our goal is to improve the readability of the API description with the emphasis of having a clear description for Schema Components.",[10,477,478],{},"Now we validate if the description property is present, we’ll check if our agreed upon template is used to formulate the functional description.",[183,480,482],{"className":252,"code":481,"language":254,"meta":188,"style":188},"{\n  \"description\": \"Object policies\",\n  \"rules\": {\n    \"object-policies:preferred-description-template\": {\n      \"description\": \"Object description must implement template\",\n      \"message\": \"Should contain 'A ... is a ... that ...' template\",\n      \"given\": [\"$..schemas[*].description\"],\n      \"severity\": \"warn\",\n      \"then\": {\n        \"function\": \"pattern\",\n        \"functionOptions\": {\n          \"match\": \"(A|An)\\\\s.+\\\\sis\\\\sa\\\\s.+\\\\sthat\\\\s.+\"\n        }\n      }\n    }\n  }\n}\n",[146,483,484,488,498,504,511,522,533,544,555,561,572,579,620,625,629,633,638],{"__ignoreMap":188},[66,485,486],{"class":193,"line":194},[66,487,262],{"class":261},[66,489,490,492,494,496],{"class":193,"line":265},[66,491,269],{"class":268},[66,493,272],{"class":261},[66,495,275],{"class":201},[66,497,278],{"class":261},[66,499,500,502],{"class":193,"line":281},[66,501,284],{"class":268},[66,503,287],{"class":261},[66,505,506,509],{"class":193,"line":290},[66,507,508],{"class":268},"    \"object-policies:preferred-description-template\"",[66,510,287],{"class":261},[66,512,513,515,517,520],{"class":193,"line":298},[66,514,301],{"class":268},[66,516,272],{"class":261},[66,518,519],{"class":201},"\"Object description must implement template\"",[66,521,278],{"class":261},[66,523,524,526,528,531],{"class":193,"line":311},[66,525,314],{"class":268},[66,527,272],{"class":261},[66,529,530],{"class":201},"\"Should contain 'A ... is a ... that ...' template\"",[66,532,278],{"class":261},[66,534,535,537,539,542],{"class":193,"line":323},[66,536,326],{"class":268},[66,538,329],{"class":261},[66,540,541],{"class":201},"\"$..schemas[*].description\"",[66,543,335],{"class":261},[66,545,546,548,550,553],{"class":193,"line":338},[66,547,341],{"class":268},[66,549,272],{"class":261},[66,551,552],{"class":201},"\"warn\"",[66,554,278],{"class":261},[66,556,557,559],{"class":193,"line":351},[66,558,354],{"class":268},[66,560,287],{"class":261},[66,562,563,565,567,570],{"class":193,"line":359},[66,564,375],{"class":268},[66,566,272],{"class":261},[66,568,569],{"class":201},"\"pattern\"",[66,571,278],{"class":261},[66,573,574,577],{"class":193,"line":372},[66,575,576],{"class":268},"        \"functionOptions\"",[66,578,287],{"class":261},[66,580,581,584,586,589,593,596,598,601,603,606,608,610,612,615,617],{"class":193,"line":383},[66,582,583],{"class":268},"          \"match\"",[66,585,272],{"class":261},[66,587,588],{"class":201},"\"(A|An)",[66,590,592],{"class":591},"suJrU","\\\\",[66,594,595],{"class":201},"s.+",[66,597,592],{"class":591},[66,599,600],{"class":201},"sis",[66,602,592],{"class":591},[66,604,605],{"class":201},"sa",[66,607,592],{"class":591},[66,609,595],{"class":201},[66,611,592],{"class":591},[66,613,614],{"class":201},"sthat",[66,616,592],{"class":591},[66,618,619],{"class":201},"s.+\"\n",[66,621,622],{"class":193,"line":389},[66,623,624],{"class":261},"        }\n",[66,626,627],{"class":193,"line":395},[66,628,386],{"class":261},[66,630,631],{"class":193,"line":401},[66,632,392],{"class":261},[66,634,636],{"class":193,"line":635},16,[66,637,398],{"class":261},[66,639,641],{"class":193,"line":640},17,[66,642,404],{"class":261},[10,644,645],{},"We use a built-in function to apply a Regex to the content of the description property of each available Schema.",[10,647,648],{},"The intention of this validation rule is to give warnings to developers that their Schema description is lacking a functional description template that recommend to use to achieve consistent definitions that are more likely to be understood by external developers.",[10,650,651],{},[213,652],{"alt":653,"src":654},"Running lint command with output","\u002Farticles\u002Fautomate-api-governance\u002Fspectral-4.jpg",[10,656,657],{},"If we run the linter again, we’ll see the new finding listed as a warning within the result.",[10,659,660],{},"That’s it!",[10,662,663],{},"There are many tools to choose from, each with their own quirks, positives and negatives. As well as a million different rules you could import, write and implement.",[31,665,667],{"id":666},"closing-thoughts","Closing Thoughts",[10,669,670],{},"Governance is hard.",[10,672,673],{},"Finding the balance between enforcing, reviewing, coaching and guiding will be different for each team, department or organisation. Unfortunately, there’s no single approach that works everywhere.",[10,675,676],{},"Though every time we’ve found it worthwhile to involve those involved and come to a collaborative approach for solving a part of the governance puzzle.",[10,678,679],{},"Start with a pilot that applies the most important — and easily implemented — policy the teams agree on and go from there.",[10,681,682],{},"Eventually, especially within larger organisations you’ll eventually be setting up a center of enablement or expert team on this topic. With the sole purpose to facilitate others in maximizing API-enable value. They shouldn’t become a regulatory bottleneck instead; they focus on alleviating impediments and constraints for other.",[10,684,685],{},"Engineers should be as autonomous as possible and as expert as needed.",[10,687,688],{},[51,689,690],{},"For more on Spectral and its capabilities and limitations reference their documentation. It’s worth noting that there are other viable options: API-fiddle, Vacuum, Optic amongst others. Don’t fret too much on WHAT tool to use, focus on IF and WHY such a tool might be needed.",[692,693,694],"style",{},"html pre.shiki code .sQhOw, html code.shiki .sQhOw{--shiki-default:#FFA657}html pre.shiki code .s9uIt, html code.shiki .s9uIt{--shiki-default:#A5D6FF}html pre.shiki code .sFSAA, html code.shiki .sFSAA{--shiki-default:#79C0FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sZEs4, html code.shiki .sZEs4{--shiki-default:#E6EDF3}html pre.shiki code .sPWt5, html code.shiki .sPWt5{--shiki-default:#7EE787}html pre.shiki code .suJrU, html code.shiki .suJrU{--shiki-default:#FF7B72}",{"title":188,"searchDepth":265,"depth":265,"links":696},[697,698,699,700,701],{"id":33,"depth":265,"text":34},{"id":57,"depth":265,"text":58},{"id":87,"depth":265,"text":88},{"id":164,"depth":265,"text":165},{"id":666,"depth":265,"text":667},"2025-01-02","A practical example of using linting tooling to automate your API governance. Free developers from having to remember each individual convention and design rule by automating the process.","md","\u002Farticles\u002Fautomate-api-governance\u002Fcover.png",null,{},true,"\u002Farticles\u002Fautomate-api-governance",{"title":5,"description":703},"automate-api-governance","articles\u002Fautomate-api-governance",[714,715],"governance","linting","zY9ofpRDhNA1eqAaEo5IgRnC0gQU7xyXqxhZsPLh2uY",1787344419740]