{"id":1094,"date":"2026-08-17T00:48:06","date_gmt":"2026-08-17T00:48:06","guid":{"rendered":"https:\/\/www.devopsschool.com\/tutorials\/?p=1094"},"modified":"2026-08-17T00:48:07","modified_gmt":"2026-08-17T00:48:07","slug":"go-tutorials-go-testing-benchmarking-and-profiling","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/tutorials\/go-tutorials-go-testing-benchmarking-and-profiling\/","title":{"rendered":"Go Tutorials: Go Testing, Benchmarking, and Profiling"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">These three topics become much easier once you separate the questions they answer:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Topic<\/th><th>Main question<\/th><th>Main Go tool<\/th><th>Result<\/th><\/tr><\/thead><tbody><tr><td><strong>Testing<\/strong><\/td><td>Does my code work correctly?<\/td><td><code>go test<\/code><\/td><td>PASS \/ FAIL<\/td><\/tr><tr><td><strong>Benchmarking<\/strong><\/td><td>How fast is this code?<\/td><td><code>go test -bench<\/code><\/td><td>ns\/op, allocations<\/td><\/tr><tr><td><strong>Profiling<\/strong><\/td><td>Where is my program spending CPU\/memory?<\/td><td><code>go tool pprof<\/code><\/td><td>Hot functions \/ bottlenecks<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">A useful analogy:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Testing = examiner<\/strong> \u2192 \u201cIs the answer correct?\u201d<\/li>\n\n\n\n<li><strong>Benchmarking = stopwatch<\/strong> \u2192 \u201cHow long does it take?\u201d<\/li>\n\n\n\n<li><strong>Profiling = heat map<\/strong> \u2192 \u201cWhere is the time being spent?\u201d<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Go has built-in support for all three, so the examples below require <strong>no third-party testing or profiling library<\/strong>. <code>go test<\/code>, the <code>testing<\/code> package, <code>runtime\/pprof<\/code>, and <code>go tool pprof<\/code> are part of the Go toolchain. (<a href=\"https:\/\/go.dev\/doc\/tutorial\/add-a-test?utm_source=chatgpt.com\">Go.dev<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">0. Before starting<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Verify Go:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>go version<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s essentially all the installation you need for this tutorial.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There is:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>no testing framework to install<\/li>\n\n\n\n<li>no benchmark package to install<\/li>\n\n\n\n<li>no profiler to install<\/li>\n\n\n\n<li>no configuration file needed<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">For each example we&#8217;ll create a tiny Go module using:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>go mod init ...<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">1. Testing<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">What?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Testing checks whether your program produces the <strong>correct result<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>2 + 3 should equal 5<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A test automatically calls your function and checks that expectation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In Go, test files normally end with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>_test.go<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">and test functions use <code>*testing.T<\/code>. The <code>go test<\/code> command discovers and runs them automatically. (<a href=\"https:\/\/go.dev\/doc\/tutorial\/add-a-test?utm_source=chatgpt.com\">Go.dev<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Why?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Without automated tests, you might change some code and accidentally break something that worked yesterday.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Testing gives you confidence that:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>change code\n    \u2193\nrun tests\n    \u2193\nstill works<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">When?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Run tests:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>while developing<\/li>\n\n\n\n<li>after changing code<\/li>\n\n\n\n<li>before committing code<\/li>\n\n\n\n<li>before releasing software<\/li>\n\n\n\n<li>after fixing bugs<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Where?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Normally tests sit beside the code they test:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>testing-demo\/\n\u251c\u2500\u2500 go.mod\n\u251c\u2500\u2500 calculator.go\n\u2514\u2500\u2500 calculator_test.go<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">How?<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1 \u2014 Create the project<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>mkdir testing-demo\ncd testing-demo\ngo mod init example.com\/testing-demo<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Complete example<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><code>calculator.go<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>package calculator\n\n\/\/ Add returns the sum of two integers.\nfunc Add(a, b int) int {\n\treturn a + b\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><code>calculator_test.go<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>package calculator\n\nimport \"testing\"\n\nfunc TestAdd(t *testing.T) {\n\t\/\/ Call the real function.\n\tgot := Add(2, 3)\n\n\t\/\/ This is the result we expect.\n\twant := 5\n\n\t\/\/ Compare actual result with expected result.\n\tif got != want {\n\t\tt.Fatalf(\"Add(2, 3) = %d; want %d\", got, want)\n\t}\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s the entire test.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Run it<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>go test<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You should see something similar to:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>PASS\nok      example.com\/testing-demo<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For more detail:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>go test -v<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>=== RUN   TestAdd\n--- PASS: TestAdd (0.00s)\nPASS\nok      example.com\/testing-demo<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>-v<\/code> requests verbose test output. (<a href=\"https:\/\/pkg.go.dev\/cmd\/go\/internal\/test?utm_source=chatgpt.com\">Go Packages<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Understand the important lines<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">This creates a test<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>func TestAdd(t *testing.T)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Think:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Test + FunctionName<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Call your real code<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>got := Add(2, 3)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>got<\/code> means:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">What did my program actually produce?<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Define what you expect<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>want := 5<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>want<\/code> means:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">What should the program produce?<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Compare them<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>if got != want {<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>got = 5\nwant = 5<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">everything is fine.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>got = 4\nwant = 5<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">the test fails.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Fail the test<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>t.Fatalf(...)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This tells Go:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>STOP \u2014 this test failed<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Testing mental model<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Remember:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>INPUT\n  \u2193\nFUNCTION\n  \u2193\nACTUAL RESULT\n  \u2193\ncompare\n  \u2193\nEXPECTED RESULT<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Testing cares about <strong>correctness<\/strong>, not speed.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Testing commands to remember<\/h1>\n\n\n\n<pre class=\"wp-block-code\"><code># Run tests in current package\ngo test\n\n# Detailed output\ngo test -v\n\n# Test every package below current directory\ngo test .\/...<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Go&#8217;s tooling recognizes <code>_test.go<\/code> files and runs compatible test functions through <code>go test<\/code>. (<a href=\"https:\/\/go.dev\/doc\/tutorial\/add-a-test?utm_source=chatgpt.com\">Go.dev<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">2. Benchmarking<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Now suppose your function is correct.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Next question:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">How fast is it?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">That is benchmarking.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">What?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A benchmark repeatedly executes some code and measures its performance.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Does Add() return the correct answer?<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">you ask:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>How long does BuildMessage() take?<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Why?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Imagine you have two implementations:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Version A \u2192 5 milliseconds\nVersion B \u2192 1 millisecond<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Both may produce exactly the same answer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Testing cannot tell you which is faster.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Benchmarking can.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">When?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use benchmarking when:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>optimizing code<\/li>\n\n\n\n<li>comparing two implementations<\/li>\n\n\n\n<li>investigating performance<\/li>\n\n\n\n<li>checking allocations<\/li>\n\n\n\n<li>evaluating a performance change<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Don&#8217;t benchmark everything automatically.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Benchmark code where performance actually matters.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Where?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Benchmarks also normally live in <code>_test.go<\/code> files.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>benchmark-demo\/\n\u251c\u2500\u2500 go.mod\n\u251c\u2500\u2500 message.go\n\u2514\u2500\u2500 message_test.go<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Benchmark functions use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>func BenchmarkSomething(b *testing.B)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>go test -bench=.<\/code> command runs benchmarks; benchmarks are not run by default. (<a href=\"https:\/\/go.dev\/src\/cmd\/go\/internal\/test\/test.go?utm_source=chatgpt.com\">Go.dev<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Complete example<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1 \u2014 Create project<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>mkdir benchmark-demo\ncd benchmark-demo\ngo mod init example.com\/benchmark-demo<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><code>message.go<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>package message\n\nimport \"strings\"\n\n\/\/ BuildMessage creates a string containing \"Go\"\n\/\/ repeated n times.\nfunc BuildMessage(n int) string {\n\tvar builder strings.Builder\n\n\tfor i := 0; i &lt; n; i++ {\n\t\tbuilder.WriteString(\"Go\")\n\t}\n\n\treturn builder.String()\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><code>message_test.go<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>package message\n\nimport \"testing\"\n\n\/\/ Store the result so the compiler cannot simply\n\/\/ throw away the work being benchmarked.\nvar benchmarkResult string\n\nfunc BenchmarkBuildMessage(b *testing.B) {\n\tfor i := 0; i &lt; b.N; i++ {\n\t\tbenchmarkResult = BuildMessage(100)\n\t}\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Run the benchmark<\/h1>\n\n\n\n<pre class=\"wp-block-code\"><code>go test -bench=.<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You may see something like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>BenchmarkBuildMessage-8    1200000    850 ns\/op\nPASS<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Your numbers will probably be different.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s expected.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">CPU, OS, Go version and machine workload all affect benchmark results.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">The mysterious <code>b.N<\/code><\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">This confuses many people initially:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>for i := 0; i &lt; b.N; i++ {<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You don&#8217;t normally choose <code>b.N<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Go&#8217;s benchmark runner controls it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Conceptually:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Go: Try 1 iteration\nGo: Too quick.\n\nGo: Try more iterations\nGo: Still quick.\n\nGo: Try many iterations\nGo: Good. Now I have enough data.<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">So your benchmark simply says:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>for i := 0; i &lt; b.N; i++ {\n\tdoThingBeingMeasured()\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Measure memory too<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>go test -bench=. -benchmem<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>-benchmem<\/code> tells <code>go test<\/code> to print memory-allocation statistics for benchmarks. (<a href=\"https:\/\/pkg.go.dev\/cmd\/go\/internal\/test?utm_source=chatgpt.com\">Go Packages<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You might see:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>BenchmarkBuildMessage-8    1200000    850 ns\/op    224 B\/op    1 allocs\/op<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Read it approximately like this:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Value<\/th><th>Meaning<\/th><\/tr><\/thead><tbody><tr><td><code>1200000<\/code><\/td><td>Number of benchmark iterations<\/td><\/tr><tr><td><code>850 ns\/op<\/code><\/td><td>About 850 nanoseconds per operation<\/td><\/tr><tr><td><code>224 B\/op<\/code><\/td><td>About 224 bytes allocated per operation<\/td><\/tr><tr><td><code>1 allocs\/op<\/code><\/td><td>About 1 allocation per operation<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Benchmarking mental model<\/h1>\n\n\n\n<pre class=\"wp-block-code\"><code>FUNCTION\n   \u2193\nrun\nrun\nrun\nrun\nrun\nrun\n   \u2193\nmeasure\n   \u2193\ntime\/op\nmemory\/op\nallocations\/op<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Benchmarking cares about <strong>performance measurements<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It does not tell you <em>why<\/em> something is slow.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That brings us to profiling.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Benchmark commands to remember<\/h1>\n\n\n\n<pre class=\"wp-block-code\"><code># Run all benchmarks\ngo test -bench=.\n\n# Benchmark + memory allocations\ngo test -bench=. -benchmem<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>.<\/code> in:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>-bench=.<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">is a regular expression matching all benchmark names. Go&#8217;s current test command documentation explicitly describes <code>-bench=.<\/code> as the way to run all benchmarks. (<a href=\"https:\/\/go.dev\/src\/cmd\/go\/internal\/test\/test.go?utm_source=chatgpt.com\">Go.dev<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">3. Profiling<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">This is the one people usually confuse with benchmarking.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Suppose your benchmark tells you:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>This operation takes 500 ms.<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Great.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But <strong>why<\/strong> does it take 500 ms?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Where is that time going?<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Function A?\nFunction B?\nJSON parsing?\nDatabase work?\nString processing?\nGarbage collection?<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Profiling helps answer that.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">What?<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Profiling observes a running program and identifies where resources are being consumed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For CPU profiling:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Program uses lots of CPU\n       \u2193\nProfiler samples execution\n       \u2193\nFunction A \u2192 5%\nFunction B \u2192 10%\nFunction C \u2192 75%\n       \u2193\nFunction C is suspicious<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Go provides runtime profiling data that can be examined with <code>go tool pprof<\/code>. CPU profiles tell you where the program spends CPU time. (<a href=\"https:\/\/go.dev\/doc\/diagnostics?utm_source=chatgpt.com\">Go.dev<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Why?<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Benchmarking tells you:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">The operation is slow.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Profiling tells you:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">This particular function is consuming most of the CPU.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s the critical difference.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">When?<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Use profiling when:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>an application is slow<\/li>\n\n\n\n<li>CPU usage is high<\/li>\n\n\n\n<li>memory usage is high<\/li>\n\n\n\n<li>you know there is a performance problem but don&#8217;t know where it is<\/li>\n\n\n\n<li>benchmarks show regression and you need the cause<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Where?<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Profiling is normally performed against a <strong>realistic workload<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For learning, we&#8217;ll intentionally create a CPU-heavy program and profile it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Our folder:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>profile-demo\/\n\u251c\u2500\u2500 go.mod\n\u2514\u2500\u2500 main.go<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Complete CPU profiling example<\/h1>\n\n\n\n<h3 class=\"wp-block-heading\">Create project<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>mkdir profile-demo\ncd profile-demo\ngo mod init example.com\/profile-demo<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><code>main.go<\/code><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>package main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"runtime\/pprof\"\n\t\"time\"\n)\n\n\/\/ isPrime intentionally performs CPU work.\n\/\/ The profiler should identify this as expensive.\nfunc isPrime(n int) bool {\n\tif n &lt; 2 {\n\t\treturn false\n\t}\n\n\tfor divisor := 2; divisor*divisor &lt;= n; divisor++ {\n\t\tif n%divisor == 0 {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\n\/\/ countPrimes calls isPrime many times.\nfunc countPrimes(limit int) int {\n\tcount := 0\n\n\tfor n := 2; n &lt;= limit; n++ {\n\t\tif isPrime(n) {\n\t\t\tcount++\n\t\t}\n\t}\n\n\treturn count\n}\n\nfunc main() {\n\t\/\/ Create the file where CPU profiling data will be stored.\n\tfile, err := os.Create(\"cpu.prof\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t\/\/ Start collecting CPU profiling data.\n\tif err := pprof.StartCPUProfile(file); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t\/\/ Run CPU-heavy work for about 2 seconds.\n\tdeadline := time.Now().Add(2 * time.Second)\n\n\tresult := 0\n\truns := 0\n\n\tfor time.Now().Before(deadline) {\n\t\tresult += countPrimes(50000)\n\t\truns++\n\t}\n\n\t\/\/ Stop profiling so all profiling data is written.\n\tpprof.StopCPUProfile()\n\n\tif err := file.Close(); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Printf(\"Completed %d runs; result=%d\\n\", runs, result)\n\tfmt.Println(\"CPU profile written to cpu.prof\")\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The important profiling calls are:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pprof.StartCPUProfile(file)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">and:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pprof.StopCPUProfile()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Stopping the profile before the program exits is important so pending profiling information can be written. This is the standard pattern documented by Go. (<a href=\"https:\/\/go.dev\/blog\/pprof?utm_source=chatgpt.com\">Go.dev<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Step 1 \u2014 Build the program<\/h1>\n\n\n\n<pre class=\"wp-block-code\"><code>go build -o profile-demo .<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You&#8217;ll now have:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>profile-demo<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Step 2 \u2014 Run it<\/h1>\n\n\n\n<pre class=\"wp-block-code\"><code>.\/profile-demo<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Completed 42 runs; result=215292\nCPU profile written to cpu.prof<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You&#8217;ll now see:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>profile-demo\/\n\u251c\u2500\u2500 go.mod\n\u251c\u2500\u2500 main.go\n\u251c\u2500\u2500 profile-demo\n\u2514\u2500\u2500 cpu.prof<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>cpu.prof<\/code> contains the profiling information.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Step 3 \u2014 Analyze the profile<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Here is our profiling tool:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>go tool pprof<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>go tool pprof -top .\/profile-demo cpu.prof<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The documented <code>pprof<\/code> command accepts the program binary and profile data for analysis. (<a href=\"https:\/\/pkg.go.dev\/cmd\/pprof?utm_source=chatgpt.com\">Go Packages<\/a>)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You should see output roughly resembling:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Showing nodes accounting for 1.92s, 96% of 2.00s total\n\n      flat  flat%   sum%        cum   cum%\n     1.70s 85.00% 85.00%      1.70s 85.00%  main.isPrime\n     0.15s  7.50% 92.50%      1.85s 92.50%  main.countPrimes<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Your exact numbers will differ.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The important part is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>main.isPrime<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If that function consumes most of the CPU, you have discovered your <strong>hotspot<\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Understanding <code>flat<\/code> and <code>cum<\/code><\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Two useful columns are:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>flat<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">CPU time spent directly inside that function.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>main.isPrime    1.70s<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><code>cum<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Cumulative time:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>function itself\n+\nfunctions it called<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You don&#8217;t need to memorize every <code>pprof<\/code> column yet.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Initially ask:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Which functions are at the top?<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s enough to get started.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Profiling mental model<\/h1>\n\n\n\n<pre class=\"wp-block-code\"><code>             PROGRAM\n                \u2502\n                \u25bc\n        \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n        \u2502 CPU activity   \u2502\n        \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                \u2502\n                \u25bc\n          runtime\/pprof\n                \u2502\n                \u25bc\n            cpu.prof\n                \u2502\n                \u25bc\n        go tool pprof\n                \u2502\n                \u25bc\n        HOT FUNCTIONS<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Profiling cares about <strong>where resources are being spent<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Go&#8217;s diagnostics documentation describes profiling specifically as a way to identify expensive or frequently executed parts of a program. (<a href=\"https:\/\/go.dev\/doc\/diagnostics?utm_source=chatgpt.com\">Go.dev<\/a>)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">The three together<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">This is the workflow I recommend remembering:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TD\n    A&#91;Write Go code] --&gt; B&#91;Testing]\n    B --&gt; C{Correct?}\n    C --&gt;|No| D&#91;Fix code]\n    D --&gt; B\n    C --&gt;|Yes| E&#91;Benchmarking]\n    E --&gt; F{Performance good?}\n    F --&gt;|Yes| G&#91;Done]\n    F --&gt;|No| H&#91;Profiling]\n    H --&gt; I&#91;Find hotspot]\n    I --&gt; J&#91;Optimize hotspot]\n    J --&gt; B<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Notice something important:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Profiling does not replace benchmarking. Benchmarking does not replace testing.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">They solve different problems.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Testing vs Benchmarking vs Profiling<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th><\/th><th>Testing<\/th><th>Benchmarking<\/th><th>Profiling<\/th><\/tr><\/thead><tbody><tr><td>Main purpose<\/td><td>Correctness<\/td><td>Measure performance<\/td><td>Find performance bottleneck<\/td><\/tr><tr><td>Main question<\/td><td>Does it work?<\/td><td>How fast is it?<\/td><td>Where is time\/resources going?<\/td><\/tr><tr><td>Standard Go tool<\/td><td><code>go test<\/code><\/td><td><code>go test -bench<\/code><\/td><td><code>go tool pprof<\/code><\/td><\/tr><tr><td>Go API<\/td><td><code>testing.T<\/code><\/td><td><code>testing.B<\/code><\/td><td><code>runtime\/pprof<\/code><\/td><\/tr><tr><td>Typical file<\/td><td><code>_test.go<\/code><\/td><td><code>_test.go<\/code><\/td><td>application code\/profile<\/td><\/tr><tr><td>Output<\/td><td>PASS\/FAIL<\/td><td>ns\/op<\/td><td>hotspots<\/td><\/tr><tr><td>Memory info<\/td><td>Not primary purpose<\/td><td><code>-benchmem<\/code><\/td><td>memory profiles possible<\/td><\/tr><tr><td>Runs code repeatedly?<\/td><td>Usually a defined test case<\/td><td>Yes, many times<\/td><td>Observes executing program<\/td><\/tr><tr><td>Finds bugs?<\/td><td>Yes<\/td><td>Not primarily<\/td><td>Not primarily<\/td><\/tr><tr><td>Finds slow code?<\/td><td>No<\/td><td>Tells you something is slow<\/td><td>Tells you where it is slow<\/td><\/tr><tr><td>Best time to use<\/td><td>Constantly<\/td><td>Performance work<\/td><td>Performance investigation<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">One example situation<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Imagine an HTTP endpoint:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GET \/products<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Users say it is slow.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">First: Testing<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">You test:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Does \/products return the correct products?<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Result:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>PASS<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">So correctness looks good.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Second: Benchmarking<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">You benchmark:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>How long does product processing take?<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Result:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>50 ms\/op<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s slower than you&#8217;d like.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But you still don&#8217;t know <strong>why<\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Third: Profiling<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Profile the program.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You discover:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>parseProducts()        5%\nsortProducts()        80%\nformatResponse()       10%\nother                  5%<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now you know:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sortProducts()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">is where you should investigate.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s the relationship between the three.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">The commands you actually need to memorize<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">For now, memorize only these six:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Testing<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>go test<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>go test -v<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>go test .\/...<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Benchmarking<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>go test -bench=.<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>go test -bench=. -benchmem<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Profiling<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>go tool pprof -top .\/program cpu.prof<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s enough for a solid foundation.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Final mental cheat sheet<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">When confused, ask yourself which question you are trying to answer:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\"IS MY CODE CORRECT?\"\n        \u2502\n        \u25bc\n      TEST\n   go test\n\n\n\"HOW FAST IS MY CODE?\"\n        \u2502\n        \u25bc\n    BENCHMARK\n go test -bench=.\n\n\n\"WHY\/WHERE IS IT SLOW?\"\n        \u2502\n        \u25bc\n     PROFILE\n go tool pprof<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Or even shorter:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Testing      = correctness\nBenchmarking = measurement\nProfiling    = investigation<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Recommended learning order<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Don&#8217;t try to master all three simultaneously. Learn them in exactly this sequence:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1. testing.T\n      \u2193\n2. testing.B\n      \u2193\n3. runtime\/pprof + go tool pprof<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Once that distinction is completely clear, more advanced topics such as <strong>table-driven tests, mocks, coverage, subtests, benchmark comparisons, memory profiling, HTTP pprof, tracing, race detection, and fuzzing<\/strong> become much easier to understand.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>These three topics become much easier once you separate the questions they answer: Topic Main question Main Go tool Result Testing Does my code work correctly? go&#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1094","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/1094","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/comments?post=1094"}],"version-history":[{"count":1,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/1094\/revisions"}],"predecessor-version":[{"id":1095,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/1094\/revisions\/1095"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/media?parent=1094"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/categories?post=1094"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/tags?post=1094"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}