A small server you can grow without rewriting at midnight
Let me tell you how most “simple” Go servers die.
Day one: you ship a tiny API. One file. Three routes. Clean. You feel like a responsible adult.
Day fourteen: product asks for “just one more endpoint.” You add it.
Day twenty: you add auth. You copy paste auth into every handler because it is faster than thinking. You promise to clean it up later.
Day thirty: you have ten endpoints and three kinds of authentication logic, depending on which coffee you were on that morning. Someone reports a bug, you fix it in one handler, and it still happens because the other handler uses the older copy pasted version.
Then you do the most dangerous thing an engineer can do.
You say, “It is fine, it is just a small server.”
That phrase is how small servers become unmaintainable servers. Not because Go is hard. Because routing becomes a junk drawer.
So in this note, we are going to build a Go server with routing that stays boring and predictable. Boring is good. Boring survives.
The case we are building
You run a tiny payments product. Merchants hit your API to create payment links, list them, and fetch one link.
You have these routes:
GET /healthso uptime monitors stop emailing you at 3amPOST /linkscreate a payment linkGET /linkslist payment linksGET /links/{id}fetch one payment link
You are going to do this using the standard library, plus a small amount of structure so you do not end up with spaghetti handlers.
Everything compiles. No hidden magic.
Step 0: Create the project