<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tecnick.com LTD on Tecnick.com</title><link>https://tecnick.com/</link><description>Recent content in Tecnick.com LTD on Tecnick.com</description><generator>Hugo</generator><language>en-gb</language><lastBuildDate>Sun, 20 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tecnick.com/index.xml" rel="self" type="application/rss+xml"/><item><title>Articles</title><link>https://tecnick.com/articles/</link><pubDate>Sun, 20 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/articles/</guid><description>&lt;p&gt;The articles on individual &lt;a href="https://nurago.org/"&gt;nurago&lt;/a&gt; packages moved to &lt;a href="https://nurago.org/articles/"&gt;nurago.org/articles/&lt;/a&gt; in September 2026, so that each one sits next to the package it describes. Their old addresses here still resolve.&lt;/p&gt;&#10;&lt;p&gt;The &lt;a href="https://tecnick.com/guides/go-rest-api/"&gt;twenty-part guide to building a production REST API in Go&lt;/a&gt; stays on this site.&lt;/p&gt;</description></item><item><title>Search</title><link>https://tecnick.com/search/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://tecnick.com/search/</guid><description/></item><item><title>Software</title><link>https://tecnick.com/software/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://tecnick.com/software/</guid><description>&lt;p&gt;Tecnick.com is strongly engaged in the development and maintenance of &lt;a href="https://opensource.org/" rel="nofollow"&gt;Open Source&lt;/a&gt; projects. Over the years we have contributed to several third-party Open Source projects and published many original ones. The actively maintained projects are listed first, followed by legacy and archived projects that are kept online for reference.&lt;/p&gt;&#10;&lt;h2 id="actively-maintained"&gt;Actively Maintained&lt;/h2&gt;&#10;&lt;h3 id="go"&gt;Go&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/tecnickcom/nurago"&gt;&lt;strong&gt;nurago&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;Collection of modular and reusable Go (golang) packages for building services and infrastructure code, including a web-service project builder.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/tecnickcom/rpistat"&gt;&lt;strong&gt;rpistat&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;Web-Service to collect system usage statistics.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/tecnickcom/rndpwd"&gt;&lt;strong&gt;rndpwd&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;Web-Service Random Password Generator written in Go (example project).&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/tecnickcom/statsd"&gt;&lt;strong&gt;statsd&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;Simple and efficient StatsD client, supporting counter, gauge, timing and set metrics with InfluxDB and Datadog tags.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="c-and-multi-language-libraries"&gt;C and Multi-language Libraries&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/tecnickcom/variantkey"&gt;&lt;strong&gt;variantkey&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;Reversible numerical encoding for human genetic variants and regions. Available for C, Go, Python, Javascript and R.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/tecnickcom/farmhash64"&gt;&lt;strong&gt;farmhash64&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;Portable multi-language implementation of the FarmHash64 and FarmHash32 non-cryptographic fingerprint hash functions. Available for C, CGO, Go, Java, Javascript, PHP, Python, R, Rust and Zig.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/tecnickcom/binsearch"&gt;&lt;strong&gt;binsearch&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;Fast binary search of unsigned integers in memory-mapped columnar binary files, including the Apache Arrow, Feather and BINSRC1 formats. Available for C, Go and Python.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="php-and-web-applications"&gt;PHP and Web Applications&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://tcexam.org"&gt;&lt;strong&gt;TCExam&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;Computer-Based Assessment (CBA) system for universities, schools and companies, that enables educators and trainers to author, schedule, deliver and report on surveys, quizzes, tests and exams.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/tecnickcom/web-cctray"&gt;&lt;strong&gt;web-cctray&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;Web-based dashboard for CI/CD cctray.xml files.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="php-libraries"&gt;PHP Libraries&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://tcpdf.org"&gt;&lt;strong&gt;tc-lib-pdf&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;PHP &lt;abbr title="Portable Document Format"&gt;PDF&lt;/abbr&gt; library (TCPDF), to generate PDF documents on-the-fly without requiring external extensions.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/tecnickcom/tc-lib-barcode"&gt;&lt;strong&gt;tc-lib-barcode&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;PHP library to generate 70+ linear, 2D and postal barcodes as &lt;abbr title="Scalable Vector Graphics"&gt;SVG&lt;/abbr&gt;, &lt;abbr title="Portable Network Graphics"&gt;PNG&lt;/abbr&gt;, &lt;abbr title="HyperText Markup Language"&gt;HTML&lt;/abbr&gt; or text.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/tecnickcom/tc-lib-pdf-font"&gt;&lt;strong&gt;tc-lib-pdf-font&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;PHP library containing PDF font methods and utilities.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/tecnickcom/tc-lib-pdf-page"&gt;&lt;strong&gt;tc-lib-pdf-page&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;PHP library containing PDF page formats and definitions.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/tecnickcom/tc-lib-pdf-image"&gt;&lt;strong&gt;tc-lib-pdf-image&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;PHP library containing PDF Image methods.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/tecnickcom/tc-lib-pdf-graph"&gt;&lt;strong&gt;tc-lib-pdf-graph&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;PHP library containing PDF graphic and geometric methods.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/tecnickcom/tc-lib-pdf-sign"&gt;&lt;strong&gt;tc-lib-pdf-sign&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;PHP library containing digital signature primitives for PDF documents (PKCS#7, CAdES, PAdES).&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/tecnickcom/tc-lib-pdf-parser"&gt;&lt;strong&gt;tc-lib-pdf-parser&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;PHP library to parse PDF documents.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/tecnickcom/tc-lib-pdf-encrypt"&gt;&lt;strong&gt;tc-lib-pdf-encrypt&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;PHP library to encrypt data for PDF documents.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/tecnickcom/tc-lib-pdf-filter"&gt;&lt;strong&gt;tc-lib-pdf-filter&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;PHP library to decode PDF compression and encryption filters.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/tecnickcom/tc-lib-unicode"&gt;&lt;strong&gt;tc-lib-unicode&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;PHP library containing Unicode methods, including &lt;abbr title="Unicode Transformation Format, 8-bit"&gt;UTF-8&lt;/abbr&gt; conversions, the Unicode Bidirectional Algorithm (UAX #9) and script-specific character substitutions.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/tecnickcom/tc-lib-unicode-data"&gt;&lt;strong&gt;tc-lib-unicode-data&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;PHP library containing UTF-8 font definitions.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/tecnickcom/tc-lib-file"&gt;&lt;strong&gt;tc-lib-file&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;PHP library to read byte-level data from files.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/tecnickcom/tc-lib-color"&gt;&lt;strong&gt;tc-lib-color&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&#10;PHP library to parse, convert and format the color representations used in web and PDF output.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="font-assets"&gt;Font Assets&lt;/h3&gt;&#10;&lt;p&gt;Data-only packages that provide the font resources used by the PDF toolchain.&lt;/p&gt;</description></item><item><title>Websites</title><link>https://tecnick.com/websites/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://tecnick.com/websites/</guid><description>&lt;p&gt;Tecnick.com owns and manages a portfolio of technology-focused websites that provide open resources, practical tools, and technical documentation for developers, engineers, students, and digital makers.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://technick.net"&gt;&lt;strong&gt;technick.net&lt;/strong&gt;&lt;/a&gt; - Guides and online tools for electronics, hardware and software&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://allpinouts.org"&gt;&lt;strong&gt;allpinouts.org&lt;/strong&gt;&lt;/a&gt; - Archive of cable and connector pinouts&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://circuitsarchive.org"&gt;&lt;strong&gt;circuitsarchive.org&lt;/strong&gt;&lt;/a&gt; - Archive of electronic circuit schematics and projects&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://nurago.org"&gt;&lt;strong&gt;nurago.org&lt;/strong&gt;&lt;/a&gt; - Open Source collection of modular Go packages for backend services&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://tcpdf.org"&gt;&lt;strong&gt;tcpdf.org&lt;/strong&gt;&lt;/a&gt; - Open Source PHP library to generate &lt;abbr title="Portable Document Format"&gt;PDF&lt;/abbr&gt; documents (TCPDF and tc-lib-pdf)&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://tcexam.org"&gt;&lt;strong&gt;tcexam.org&lt;/strong&gt;&lt;/a&gt; - Open Source Computer-Based Assessment (CBA) system&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://testimages.org"&gt;&lt;strong&gt;testimages.org&lt;/strong&gt;&lt;/a&gt; - Archive of free test images for research and display testing&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://atletix.net"&gt;&lt;strong&gt;atletix.net&lt;/strong&gt;&lt;/a&gt; - [IT] Guide, strumenti e calcolatori per l&amp;rsquo;attività motoria e l&amp;rsquo;atletica leggera&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://appunti.asuni.xyz"&gt;&lt;strong&gt;appunti.asuni.xyz&lt;/strong&gt;&lt;/a&gt; - [IT] Appunti universitari di Nicola Asuni&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://nicola.asuni.xyz"&gt;&lt;strong&gt;nicola.asuni.xyz&lt;/strong&gt;&lt;/a&gt; - Nicola Asuni&amp;rsquo;s personal website and professional profile&lt;/li&gt;&#10;&lt;/ul&gt;</description></item><item><title>Guides</title><link>https://tecnick.com/guides/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;Long-form, multi-part guides. Where an &lt;a href="https://tecnick.com/articles/"&gt;article&lt;/a&gt; takes one subject&#10;apart, a guide builds one thing from start to finish.&lt;/p&gt;&#10;&lt;h2 id="building-a-production-rest-api-in-go"&gt;&lt;a href="https://tecnick.com/guides/go-rest-api/"&gt;Building a Production REST API in Go&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;Twenty parts, in six sections, building a booking service in Go from a bound&#10;socket to a deployment manifest. Configuration, startup and shutdown, logging and&#10;redaction, servers and middleware, the request lifecycle, response formats,&#10;outbound clients and retries, connection pools and transactions, health and&#10;metrics, one complete feature with its write and read paths, four layers of&#10;testing, packaging, and the probes that watch it in production.&lt;/p&gt;</description></item><item><title>Legal</title><link>https://tecnick.com/legal/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://tecnick.com/legal/</guid><description>&lt;h2&gt;Disclaimer&lt;/h2&gt;&#10;&#10;&lt;p&gt;&lt;strong&gt;Please be aware that any information you find on this website may be inaccurate, misleading or out of date.&lt;/strong&gt;&lt;/p&gt;&#10;&#10;&lt;p&gt;Some information on Tecnick.com may create a risk for readers who choose to apply or use it in their own activities, or who promote it for use by third parties. None of the authors or contributors connected with Tecnick.com can be held responsible, in any way whatsoever, for your use of the information contained in or linked from these pages.&lt;/p&gt;</description></item><item><title>TCPDF: 25 Years of Generating PDFs in PHP</title><link>https://tecnick.com/articles/tcpdf/</link><pubDate>Sun, 28 Jun 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/articles/tcpdf/</guid><description>&lt;hr&gt;&#10;&lt;img src="https://tecnick.com/img/tc-lib-pdf_journey_640x272.png" alt="tecnickcom/tc-lib-pdf" width="640" height="300" /&gt;&#10;&lt;p&gt;Many PHP applications end up needing to produce a &lt;abbr title="Portable Document Format"&gt;PDF&lt;/abbr&gt;. An invoice, a shipping label, a course certificate, a signed contract, a monthly report. It is one of those unglamorous requirements that turns up on project after project, and it is rarely as simple as it first sounds.&lt;/p&gt;&#10;&lt;p&gt;I know, because I have been writing the code that does it for twenty-five years.&lt;/p&gt;&#10;&lt;p&gt;If you have generated a PDF from PHP at any point in the last two decades, there is a good chance some of my code was involved. TCPDF, the library I started in 2002, has been installed more than 109 million times through Composer alone, and it is a dependency of a long list of applications you already know. I have even found documents generated by TCPDF sitting on NASA&amp;rsquo;s own data-centre websites. More on that below.&lt;/p&gt;</description></item><item><title>Software Quality Is a System, Not an Act: Inside the Quality Pipeline of nurago</title><link>https://tecnick.com/articles/software-quality/</link><pubDate>Sat, 11 Jul 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/articles/software-quality/</guid><description>&lt;hr&gt;&#10;&lt;img src="https://tecnick.com/img/quality_pipeline_640x300.png" alt="tecnickcom/nurago quality pipeline" width="640" height="300" /&gt;&#10;&lt;p&gt;Saying a project cares about software quality is easy. Pointing at the exact mechanisms that produce it is harder.&lt;/p&gt;&#10;&lt;p&gt;Quality is not a final inspection step, and it is not a property you can bolt on before a release. It is the cumulative result of dozens of small, boring, automated decisions that either happen on every commit or do not happen at all.&lt;/p&gt;&#10;&lt;p&gt;That is what the title of this article means. An act is something a person performs at a moment in time: a &lt;abbr title="Quality Assurance"&gt;QA&lt;/abbr&gt; phase before the release, a yearly security audit, a reviewer being especially careful on a good day. Acts depend on memory, time, and goodwill, and they are the first casualty of deadline pressure; their results start decaying the moment they end. A system is a standing structure that produces quality as its normal output: automated, continuous, redundant, and measured, regardless of who is busy that week. W. Edwards Deming made the underlying point about manufacturing decades ago: you cannot inspect quality into a product; you have to build it into the process that makes it.&lt;/p&gt;</description></item><item><title>Building a Production REST API in Go</title><link>https://tecnick.com/guides/go-rest-api/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/go-rest-api/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;Most guides to writing an &lt;abbr title="HyperText Transfer Protocol"&gt;HTTP&lt;/abbr&gt; service in Go stop at the handler. It is the part&#10;that is easiest to teach, and the smallest part of what it takes to run the&#10;service.&lt;/p&gt;&#10;&lt;p&gt;Production is not the environment the code was written in. The process does not&#10;run once on a laptop under a developer&amp;rsquo;s eye: it is started, restarted, scaled to&#10;many copies, rescheduled onto another node and killed mid-request by an&#10;orchestrator that knows nothing about what it was doing. It runs against&#10;configuration nobody re-read, on dependencies that are up but slow, under load&#10;that arrives in bursts, from clients that retry. Its failures are rarely a clean&#10;crash and usually a degradation: a few instances answering slowly, a connection&#10;pool exhausted since the last deploy, a timeout budget larger than the caller&amp;rsquo;s&#10;patience. Nobody is attached to the process, so what you learn about it&#10;afterwards is only what it wrote down while it was still working.&lt;/p&gt;</description></item><item><title>What a REST Service Is Made Of</title><link>https://tecnick.com/guides/go-rest-api/overview/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/go-rest-api/overview/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;A &lt;abbr title="Representational State Transfer"&gt;REST&lt;/abbr&gt; service is one operating system process. It starts, reads its&#10;configuration, opens some sockets and some database connections, then accepts&#10;connections until something tells it to stop. Everything else in this guide is a&#10;detail of that sentence.&lt;/p&gt;&#10;&lt;p&gt;From the outside it is a set of URLs that return &lt;abbr title="JavaScript Object Notation"&gt;JSON&lt;/abbr&gt;, which says little about&#10;the thing you have to deploy, watch and debug at an inconvenient hour.&lt;/p&gt;</description></item><item><title>The Standard Library Underneath All of This</title><link>https://tecnick.com/guides/go-rest-api/go-http-basics/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/go-rest-api/go-http-basics/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;Go&amp;rsquo;s &lt;abbr title="HyperText Transfer Protocol"&gt;HTTP&lt;/abbr&gt; server is in the standard library, it is production quality, and six&#10;types do most of the work. Learn them here and most of what a framework, router or&#10;toolkit adds later reads as a layer over them.&lt;/p&gt;&#10;&lt;p&gt;This page is entirely &lt;code&gt;net/http&lt;/code&gt;, &lt;code&gt;net&lt;/code&gt; and &lt;code&gt;context&lt;/code&gt;. No dependencies, no&#10;toolkit, nothing from the guide&amp;rsquo;s reference implementation.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://tecnick.com/img/guides/go-rest-api/D02-stdlib-layers.svg" alt="Layer diagram of the Go standard library HTTP stack, from the bound socket at the bottom to the handler at the top. The layers are named in the sections below." width="484" height="442" loading="eager" fetchpriority="high"&gt;&#10;&lt;/p&gt;</description></item><item><title>From main to Listening</title><link>https://tecnick.com/guides/go-rest-api/startup/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/go-rest-api/startup/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;Startup is a sequence of steps that can each fail, arranged so that the ones most&#10;likely to fail happen first and report clearly. Get the order wrong and a service&#10;crashes with a nil pointer dereference and no log line, because logging was&#10;configured after the thing that broke.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://tecnick.com/img/guides/go-rest-api/D03-startup-sequence.svg" alt="Sequence diagram of the startup path, from main through cli.New and Execute to bootstrap, bind and three started servers. Each step is described in the sections below." width="927" height="1065" loading="eager" fetchpriority="high"&gt;&#10;&lt;/p&gt;</description></item><item><title>Configuration That Is Wrong Before It Is Late</title><link>https://tecnick.com/guides/go-rest-api/configuration/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/go-rest-api/configuration/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;The same binary has to run on a laptop with a &lt;abbr title="JavaScript Object Notation"&gt;JSON&lt;/abbr&gt; file next to it, in &lt;abbr title="Continuous Integration"&gt;CI&lt;/abbr&gt; with a&#10;handful of environment variables, and in production with secrets injected by a&#10;platform the developer has never logged into. It has to behave the same way in&#10;all three, and when it cannot start it has to say which value was wrong.&lt;/p&gt;&#10;&lt;p&gt;A configuration mistake usually does not stop the service. It starts a service&#10;that is subtly wrong: the read pool pointed at the write database, a timeout set&#10;to zero, the thing you needed disabled, and nobody finds out until the behaviour&#10;is strange enough to investigate.&lt;/p&gt;</description></item><item><title>The Lifecycle: One Context, One Channel, One Wait Group</title><link>https://tecnick.com/guides/go-rest-api/lifecycle/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/go-rest-api/lifecycle/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;&lt;code&gt;srv.Shutdown(ctx)&lt;/code&gt; stops the server accepting new connections and waits for the&#10;in-flight ones to finish. That is about a third of the problem. A real service&#10;also has a database pool that should close after the last query, background&#10;goroutines that need telling, a metrics client with buffered measurements, and an&#10;orchestrator that will send &lt;code&gt;SIGKILL&lt;/code&gt; if the process takes too long. Getting one&#10;of those wrong turns a deploy into a small outage: dropped requests, a truncated&#10;metrics window, or a pod that hangs until the grace period expires.&lt;/p&gt;</description></item><item><title>Logs You Can Search, and Secrets You Cannot Read</title><link>https://tecnick.com/guides/go-rest-api/logging/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/go-rest-api/logging/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;Logs are written for the moment when something is wrong, you have no idea what,&#10;and the only evidence is what the process happened to record. A line is useful&#10;then if it is findable, meaning machine-parseable fields rather than sentences,&#10;and safe, meaning no secret in it, because logs are copied into search systems&#10;that many more people can read than can read production.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="structured-meaning-fields"&gt;Structured, meaning fields&lt;/h2&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;// Not this.&lt;/span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;l.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;Info&lt;/span&gt;(fmt.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;Sprintf&lt;/span&gt;(&lt;span style="color:#a5d6ff"&gt;&amp;#34;item %s created with quantity %d in %dms&amp;#34;&lt;/span&gt;,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;id,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;qty,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;ms))&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;// This.&lt;/span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;l.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;InfoContext&lt;/span&gt;(ctx,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;item created&amp;#34;&lt;/span&gt;,&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;slog.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;String&lt;/span&gt;(&lt;span style="color:#a5d6ff"&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;id),&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;slog.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;Uint64&lt;/span&gt;(&lt;span style="color:#a5d6ff"&gt;&amp;#34;quantity&amp;#34;&lt;/span&gt;,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;uint64(qty)),&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;slog.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;Int64&lt;/span&gt;(&lt;span style="color:#a5d6ff"&gt;&amp;#34;duration_ms&amp;#34;&lt;/span&gt;,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;ms),&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first produces a line a human can read and a machine has to guess at. The&#10;second produces &lt;abbr title="JavaScript Object Notation"&gt;JSON&lt;/abbr&gt; with named fields, so &lt;code&gt;item_id:&amp;quot;019813a0-...&amp;quot;&lt;/code&gt; is a query rather&#10;than a substring search that also matches every other identifier of that shape.&#10;The message stays a short fixed string and everything variable becomes an&#10;attribute, because a message that interpolates values makes every occurrence&#10;unique and defeats grouping. &lt;code&gt;InfoContext&lt;/code&gt; rather than &lt;code&gt;Info&lt;/code&gt; passes the context,&#10;which is how the trace ID gets onto the record.&lt;/p&gt;</description></item><item><title>Servers, Binders, and the Routes You Get for Free</title><link>https://tecnick.com/guides/go-rest-api/servers/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/go-rest-api/servers/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;The server &lt;a href="https://tecnick.com/guides/go-rest-api/go-http-basics/"&gt;page 02&lt;/a&gt; built from &lt;code&gt;net.Listen&lt;/code&gt;,&#10;&lt;code&gt;http.Server&lt;/code&gt; and a &lt;code&gt;ServeMux&lt;/code&gt; works. What it lacks is timeouts set to something&#10;other than infinity, a route list that cannot contain a route nothing can reach,&#10;operational endpoints, and a shutdown that drains rather than drops.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="routes-are-data"&gt;Routes are data&lt;/h2&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;type&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;Route&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;{&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;Method&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;string&lt;/span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;Path&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;string&lt;/span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;Description&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;string&lt;/span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;Handler&lt;span style="color:#6e7681"&gt; &lt;/span&gt;http.HandlerFunc&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;Middleware&lt;span style="color:#6e7681"&gt; &lt;/span&gt;[]MiddlewareFn&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;DisableLogger&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;bool&lt;/span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;Timeout&lt;span style="color:#6e7681"&gt; &lt;/span&gt;time.Duration&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and a handler package returns a list of them:&lt;/p&gt;</description></item><item><title>The Middleware You Will Actually Need</title><link>https://tecnick.com/guides/go-rest-api/middleware/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/go-rest-api/middleware/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;Before writing any middleware, answer a different question: is something in front&#10;of this process already doing it? A service that implements everything itself&#10;carries code its platform already runs, and occasionally code that fights it: two&#10;components both answering a Cross-Origin Resource Sharing (&lt;abbr title="Cross-Origin Resource Sharing"&gt;CORS&lt;/abbr&gt;) preflight send&#10;two &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; headers, and a browser rejects that.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="the-boundary"&gt;The boundary&lt;/h2&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Concern&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Usually lives at&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;What the service still owns&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Transport Layer Security (&lt;abbr title="Transport Layer Security"&gt;TLS&lt;/abbr&gt;) termination&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;The edge&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Supporting TLS for direct and mesh-internal exposure, and knowing which mode it is in&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Response compression&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;The edge&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Not compressing twice, and knowing which end negotiates&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Rate limiting&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Gateway, ingress, content delivery network (&lt;abbr title="Content Delivery Network"&gt;CDN&lt;/abbr&gt;) or web application firewall (&lt;abbr title="Web Application Firewall"&gt;WAF&lt;/abbr&gt;)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;A last-resort limit for direct and internal callers&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;CORS and preflight&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;The gateway, when there is one&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Answering preflight when there is not&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Request body caps&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;The ingress, commonly&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Its own cap, because the edge cap does not cover internal callers&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Client Internet Protocol (&lt;abbr title="Internet Protocol"&gt;IP&lt;/abbr&gt;) address&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;The proxy sets the header; the platform decides the trusted hop depth&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Being told the hop count rather than guessing it&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Authentication&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Frequently an edge concern&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Reading an established identity, per &lt;a href="https://tecnick.com/guides/go-rest-api/feature-exposure/"&gt;page 17&lt;/a&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;&lt;img src="https://tecnick.com/img/guides/go-rest-api/D08-rejection-points.svg" alt="Swimlane diagram of the points where a request can be rejected, split between the edge lane and the service lane, in the order they are reached. Both lanes are described in the sections of this page." width="515" height="1763" loading="eager" fetchpriority="high"&gt;&#10;&lt;/p&gt;</description></item><item><title>One Request, All the Way Down</title><link>https://tecnick.com/guides/go-rest-api/request-lifecycle/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/go-rest-api/request-lifecycle/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;One request, from the byte arriving on the socket to the last log line, through&#10;the machinery pages &lt;a href="https://tecnick.com/guides/go-rest-api/servers/"&gt;07&lt;/a&gt; and&#10;&lt;a href="https://tecnick.com/guides/go-rest-api/middleware/"&gt;08&lt;/a&gt; assembled. Following it end to end shows&#10;which layer owns what, and it shows the deadline the request carries and how that&#10;gets spent.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="the-trace"&gt;The trace&lt;/h2&gt;&#10;&lt;p&gt;&lt;img src="https://tecnick.com/img/guides/go-rest-api/D09-request-lifecycle.svg" alt="Sequence diagram of one POST /items request through the server, the middleware chain, the router, the handler and the response writer. The nine numbered steps below follow the same order." width="1148" height="1154" loading="eager" fetchpriority="high"&gt;&#10;&lt;/p&gt;</description></item><item><title>The Envelope Argument</title><link>https://tecnick.com/guides/go-rest-api/responses/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/go-rest-api/responses/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;Two decisions here outlive most of the code around them: what shape a response&#10;body has, and how an error from four layers down becomes the right status code. A&#10;response format is a contract with every consumer you have, and changing it is a&#10;versioning event rather than a refactor.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="the-envelope"&gt;The envelope&lt;/h2&gt;&#10;&lt;p&gt;The reference service&amp;rsquo;s default response writer wraps a payload in this&#10;structure:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;type&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;Response&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;{&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;Program&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;string&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;`json:&amp;#34;program&amp;#34;`&lt;/span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;Version&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;string&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;`json:&amp;#34;version&amp;#34;`&lt;/span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;Release&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;string&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;`json:&amp;#34;release&amp;#34;`&lt;/span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;DateTime&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;string&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;`json:&amp;#34;datetime&amp;#34;`&lt;/span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;Timestamp&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;int64&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;`json:&amp;#34;timestamp&amp;#34;`&lt;/span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;Status&lt;span style="color:#6e7681"&gt; &lt;/span&gt;httputil.Status&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;`json:&amp;#34;status&amp;#34;`&lt;/span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;Code&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;int&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;`json:&amp;#34;code&amp;#34;`&lt;/span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;Message&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;string&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;`json:&amp;#34;message&amp;#34;`&lt;/span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;Data&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;any&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;`json:&amp;#34;data&amp;#34;`&lt;/span&gt;&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A success:&lt;/p&gt;</description></item><item><title>Talking to Other Services Without Losing the Thread</title><link>https://tecnick.com/guides/go-rest-api/clients/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/go-rest-api/clients/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;Every outbound call is a place the service can hang, leak a goroutine, lose the&#10;trace, or make somebody else&amp;rsquo;s outage worse, and &lt;code&gt;http.Get&lt;/code&gt; handles none of it.&#10;What a call needs is a timeout, structured logging of what went out and came back,&#10;a metric, the trace ID forwarded, and secrets kept out of the log line. Every&#10;client needs the same list, so it is built once.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="one-base-many-clients"&gt;One base, many clients&lt;/h2&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;httpClientOpts&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;:=&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;[]httpclient.Option{&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;httpclient.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;WithLogger&lt;/span&gt;(l),&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;httpclient.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;WithRoundTripper&lt;/span&gt;(m.InstrumentRoundTripper),&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;httpclient.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;WithTraceIDHeaderName&lt;/span&gt;(traceid.DefaultHeader),&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;httpclient.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;WithComponent&lt;/span&gt;(appInfo.ProgramName),&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;&#9;&lt;/span&gt;httpclient.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;WithRedactFn&lt;/span&gt;(logRedactor.BytesToString),&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Built once in &lt;code&gt;bind&lt;/code&gt; and passed to each client constructor, which appends only its&#10;own; inside &lt;code&gt;newIpifyClient&lt;/code&gt; the parameter is &lt;code&gt;baseHTTPClientOpts&lt;/code&gt;:&lt;/p&gt;</description></item><item><title>The Database Connection Is a Long-Lived Thing</title><link>https://tecnick.com/guides/go-rest-api/database/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/go-rest-api/database/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;&lt;code&gt;*sql.DB&lt;/code&gt; is a pool of connections, safe for concurrent use, created once at&#10;startup and borrowed by requests that last milliseconds. Most database problems a&#10;Go service has come from treating it as a single connection: one per request&#10;exhausts the server&amp;rsquo;s connection limit, closing it after a query destroys the&#10;pool, and copying a size from a blog post produces a service that queues under&#10;load for reasons the logs do not show.&lt;/p&gt;</description></item><item><title>Ping, Status, Metrics: Three Different Questions</title><link>https://tecnick.com/guides/go-rest-api/observability/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/go-rest-api/observability/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;&lt;code&gt;/ping&lt;/code&gt; answers &lt;strong&gt;is this process alive&lt;/strong&gt;, &lt;code&gt;/status&lt;/code&gt; &lt;strong&gt;can it do its job&lt;/strong&gt;, and&#10;&lt;code&gt;/metrics&lt;/code&gt; &lt;strong&gt;what has it been doing&lt;/strong&gt;.&lt;/p&gt;&#10;&lt;p&gt;Confusing the first two is expensive. Point readiness at &lt;code&gt;/ping&lt;/code&gt; and the&#10;orchestrator sends traffic to a pod that cannot reach its database. Point liveness&#10;at &lt;code&gt;/status&lt;/code&gt; and a database blip restarts every replica at once, turning a&#10;degradation into an outage.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="ping"&gt;/ping&lt;/h2&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;httpserver.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;WithPingHandlerFunc&lt;/span&gt;(jsx.&lt;span style="color:#d2a8ff;font-weight:bold"&gt;DefaultPingHandler&lt;/span&gt;(appInfo))&lt;span style="color:#6e7681"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Returns 200 and the envelope, touching no dependency, running no query, making no&#10;outbound call. A 200 from &lt;code&gt;/ping&lt;/code&gt; means the process is running, the listener is&#10;bound, the router works and a goroutine was available to serve the request.&lt;/p&gt;</description></item><item><title>Designing the Feature: Contract, Schema, Seams</title><link>https://tecnick.com/guides/go-rest-api/feature-design/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/go-rest-api/feature-design/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;Parts I to IV covered the machinery a service needs before it does anything for&#10;anybody. The reference implementation carries one feature on top of it: four&#10;endpoints over a table of items, in &lt;code&gt;internal/item&lt;/code&gt; and&#10;&lt;code&gt;internal/httphandlerpub/item.go&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://tecnick.com/guides/go-rest-api/#part-v-a-real-feature-end-to-end"&gt;Part V&lt;/a&gt; reads that&#10;feature, then covers what it leaves out. Every snippet in these four pages is&#10;code from the generated tree, at the path named above it. Where a section goes&#10;past what ships, it opens by saying so.&lt;/p&gt;</description></item><item><title>The Write Path: Decode, Validate, Commit</title><link>https://tecnick.com/guides/go-rest-api/feature-write-path/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/go-rest-api/feature-write-path/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;A write path takes bytes from a client that may be hostile, careless or merely&#10;retrying, and turns them into a row. Three questions sit between: is this&#10;readable, is it allowed, and is it already there.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://tecnick.com/img/guides/go-rest-api/D14-write-path.svg" alt="Sequence diagram of the item write path, from the posted body through the capped strict decoder and the quantity check to the insert and a 201, with the branches that answer 400, 422 and 409. Every step appears below." width="887" height="1305" loading="eager" fetchpriority="high"&gt;&#10;&lt;/p&gt;</description></item><item><title>The Read Path: What the Client Can Ask For</title><link>https://tecnick.com/guides/go-rest-api/feature-read-path/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/go-rest-api/feature-read-path/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;The item list takes one query parameter. That is enough to show the whole&#10;mechanism, because the interesting part of a list endpoint is not how many&#10;parameters it has: it is that the query string is a public interface a client&#10;can put anything into, and every parameter is a promise about what the database&#10;will be asked to do.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://tecnick.com/img/guides/go-rest-api/D15-read-path.svg" alt="Sequence diagram of the item list request, from parsing the limit through the service default and the indexed query to a 200 carrying a JSON array. Each step appears below." width="1212" height="1064" loading="eager" fetchpriority="high"&gt;&#10;&lt;/p&gt;</description></item><item><title>What Is Reachable From Where</title><link>https://tecnick.com/guides/go-rest-api/feature-exposure/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/go-rest-api/feature-exposure/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;&lt;a href="https://tecnick.com/guides/go-rest-api/overview/"&gt;Page 01&lt;/a&gt; said the three listeners are an&#10;enforcement mechanism, and there is now a real feature to place.&lt;/p&gt;&#10;&lt;p&gt;The question here is narrow: &lt;strong&gt;which listener is this endpoint on&lt;/strong&gt;. Who a caller&#10;is and what they may do is a different problem, and the last section says what&#10;this guide does about it.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="the-three-zones"&gt;The three zones&lt;/h2&gt;&#10;&lt;p&gt;&lt;img src="https://tecnick.com/img/guides/go-rest-api/D16-exposure-zones.svg" alt="Deployment diagram of the three listeners against their network zones, with the paths from an internet client to the private and monitoring ports marked as having no route. The table below carries the same content." width="680" height="539" loading="eager" fetchpriority="high"&gt;&#10;&lt;/p&gt;</description></item><item><title>Tests That Would Have Caught It</title><link>https://tecnick.com/guides/go-rest-api/testing/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/go-rest-api/testing/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;Four layers, each catching a class of bug the others cannot, ordered by how fast&#10;they run and how much they need.&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Layer&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Runs in&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Needs&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Catches&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Unit&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Microseconds&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Nothing&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Logic, branches, error paths&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;In-process end to end&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Milliseconds&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;A free port&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Routing, middleware, encoding, status codes&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Container integration&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Minutes&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Docker, MySQL, Flyway&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Migrations, real &lt;abbr title="Structured Query Language"&gt;SQL&lt;/abbr&gt;, wiring, configuration&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Contract and fuzz&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Minutes&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;A running service&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Spec drift, unhandled inputs&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;&lt;img src="https://tecnick.com/img/guides/go-rest-api/D17-test-pipeline.svg" alt="Activity diagram of the test pipeline in two swimlanes, one for what runs locally and in CI and one for what runs in CI only. Each stage in it has its own section below." width="526" height="859" loading="eager" fetchpriority="high"&gt;&#10;&lt;/p&gt;</description></item><item><title>Building, Packaging, and the Probes That Watch It</title><link>https://tecnick.com/guides/go-rest-api/build-deploy/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/go-rest-api/build-deploy/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;Everything from &lt;a href="https://tecnick.com/guides/go-rest-api/startup/"&gt;page 03&lt;/a&gt; onward has been the&#10;process&amp;rsquo;s own behaviour. This is the machinery that produces the binary, wraps it,&#10;and puts it somewhere it will be watched. A shutdown timeout the orchestrator&#10;ignores does nothing.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://tecnick.com/img/guides/go-rest-api/D18-build-deploy.svg" alt="Activity diagram of the build and deploy path, from the version files through the build and the checks to either a container image or a native package, and on to a running instance. Each stage has a section below." width="595" height="788" loading="eager" fetchpriority="high"&gt;&#10;&lt;/p&gt;</description></item><item><title>Growing the Service Without Growing the Wiring</title><link>https://tecnick.com/guides/go-rest-api/extending/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://tecnick.com/guides/go-rest-api/extending/</guid><description>&lt;hr&gt;&#10;&lt;p&gt;The service in this guide serves &lt;abbr title="HyperText Transfer Protocol"&gt;HTTP&lt;/abbr&gt; requests and nothing else. Growth past that&#10;follows a few recognisable shapes: scheduled work, queue work, a second data&#10;store, and secrets that cannot sit in a file, with one structural question&#10;underneath all four: how &lt;code&gt;bind&lt;/code&gt; stays readable while they arrive.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="work-that-has-to-happen-on-a-schedule"&gt;Work that has to happen on a schedule&lt;/h2&gt;&#10;&lt;p&gt;The idempotency keys from &lt;a href="https://tecnick.com/guides/go-rest-api/feature-write-path/"&gt;page 15&lt;/a&gt;&#10;accumulate, items nobody has touched in a year can be archived, and a cache warms&#10;better if something warms it. The shape is a goroutine that wakes on an interval, registered&#10;the way &lt;a href="https://tecnick.com/guides/go-rest-api/lifecycle/"&gt;page 05&lt;/a&gt; registers everything:&lt;/p&gt;</description></item></channel></rss>