MoreRSS

site iconThe Practical DeveloperModify

A constructive and inclusive social network for software developers.
Please copy the RSS to your reader, or quickly subscribe to:

Inoreader Feedly Follow Feedbin Local Reader

Rss preview of Blog of The Practical Developer

The 15 Git Commands Every Software Engineer Uses (And Why They Matter More Than You Think)

2026-01-20 14:06:19

For a long time, I thought Git was just something I had to survive.

Type a command.
Hope nothing breaks.
If it does… Google fast.

I memorized commands without understanding them.
Copied fixes from Stack Overflow.
Prayed I wouldn’t see merge conflicts.

And somehow… I kept using Git every day without ever feeling confident.

It took me a while to realize this:
Most developers don’t use all of Git.
They use a small set of commands... deeply.

And once I stopped treating Git like magic,
it became a tool I could actually trust.

git commands, git basics, version control, software engineering, developer tools

Git Isn’t Hard... It’s Just Unfamiliar

Git feels intimidating at first because it remembers everything.

Every mistake.
Every experiment.
Every “I’ll clean this later.”

And that can be scary.

But Git isn’t judging you.
It’s protecting your work, even when you don’t realize it yet.

Once I accepted that, learning Git stopped feeling like pressure…
and started feeling like control.

The Git Commands Software Engineers Actually Use Daily

You don’t need 50 commands.
You need the right 15, used calmly and intentionally.

These are the ones that show up in real projects, real teams, real days.

1. git status — Check the Current State of Your Repository

git status shows which files are modified, staged, untracked, or ready to be committed in your repository.

Common mistake:
Running other Git commands without checking git status first, this is how people commit or delete the wrong files.

git status command meme showing checking repository state before committing

2. git init — Initialize a New Git Repository

git init creates a new Git repository by adding version control tracking to a project directory.

Common mistake:
Running git init inside an already-initialized repo, creating a nested .git folder and confusing Git completely.

git init command meme about starting a new git repository

3. git clone — Copy a Remote Repository Locally

git clone downloads a remote repository and creates a full local copy, including its commit history.

Common mistake:
Cloning a repo and immediately pushing changes without understanding the branch structure.

git clone command meme about copying a remote repository locally

4. git add — Stage Changes for the Next Commit

git add moves file changes into the staging area so they can be included in the next commit.

Important variants:

  • git add .
    Adds all changes in the current directory and subfolders.
    ⚠️ If you’re inside a subfolder, parent directory files won’t be included.

  • git add *
    Adds only non-hidden files in the current directory.
    ⚠️ Skips files like .env, .gitignore.

  • git add :
    Adds all changes from the repository root, including hidden files.
    ✅ Safest option when you want everything, regardless of location.

Common mistake:
Using git add . blindly and accidentally staging files you didn’t intend to commit.

git add command meme about staging files before committing

5. git commit — Save a Snapshot of Your Changes

git commit records staged changes as a snapshot in the project’s history with a descriptive message.

Common mistake:
Writing vague messages like “update” or “fix stuff”, which makes future debugging painful.

git commit command meme about writing meaningful commit messages

6. git log — View the Commit History

git log displays a list of previous commits, showing changes, authors, and timestamps.

Common mistake:
Ignoring commit history and trying to guess when a bug was introduced.

git log command meme about scrolling through commit history

7. git diff — See What Has Changed

git diff shows line-by-line differences between file versions, commits, or branches.

Common mistake:
Skipping git diff and committing code without reviewing what actually changed.

git diff command meme about reviewing code changes before commit

8. git branch — Manage Parallel Development

git branch lets you create, list, rename, or delete branches for separate lines of development.

Common mistake:
Doing all work on main instead of creating feature branches.

git branch command meme about working on multiple branches

9. git checkout / git switch — Move Between Branches Safely

These commands allow you to move between branches.
Only git checkout can also be used to restore files from another branch or commit.

Important variants:

  • git checkout branch-name
    Old but still widely used.

  • git switch branch-name
    Newer, clearer, and safer for switching branches.

Common mistake:
Switching branches with uncommitted changes and accidentally losing work.

git checkout and git switch command meme about switching branches

10. git merge — Combine Changes From Different Branches

git merge integrates changes from one branch into another, combining their histories.

Common mistake:
Merging without pulling the latest changes first, leading to unnecessary conflicts.

git merge command meme about resolving merge conflicts

11. git pull — Update Your Local Repository

git pull fetches changes from a remote repository and integrates them into your current branch (by merge or rebase, depending on configuration)

Common mistake:
Pulling into a dirty working directory instead of committing or stashing first.

git pull command meme about updating local code from remote

12. git push — Share Your Commits With Others

git push uploads your local commits to a remote repository so others can access them.

Common mistake:
Forgetting to pull before pushing, causing rejected pushes and confusion.

git push command meme about sending commits to remote repository

13. git stash — Temporarily Save Unfinished Work

git stash stores uncommitted changes so you can return to a clean working directory.

Important variants:

  • git stash
    Saves changes and cleans the working directory.

  • git stash pop
    Restores the most recent stash and removes it.

  • git stash list
    Shows all saved stashes.

Common mistake:
Stashing changes and forgetting they exist.

git stash command meme about saving unfinished work temporarily

14. git reset — Undo Changes With Control

git reset moves the current branch pointer and optionally updates the staging area and working directory.

Important variants:

  • git reset --soft HEAD~1
    Keeps changes staged.

  • git reset --mixed HEAD~1 (default)
    Keeps changes unstaged.

  • git reset --hard HEAD~1
    Deletes changes permanently.

Common mistake:
Using --hard without understanding that it permanently removes work.

git reset command meme about undoing commits carefully

15. git revert — Undo Changes Safely in Shared History

git revert creates a new commit that reverses the effects of a previous commit without rewriting history.

Common mistake:
Using git reset on shared branches instead of git revert, rewriting history for teammates.

git revert command meme about safely undoing changes

Git Confidence Comes Quietly

Here’s the thing nobody tells you:

Git confidence doesn’t arrive suddenly.

It grows slowly.
After mistakes.
After conflicts.
After fixing something you thought was broken forever.

One day, you stop panicking.
You pause.
You check git status.
And you move forward calmly.

That’s progress.

What I No Longer Do With Git

❌ I don’t copy commands blindly
❌ I don’t fear breaking things
❌ I don’t rush through conflicts
❌ I don’t treat Git like magic

Git isn’t something to fight.
It’s something to understand... one command at a time.

git commands, git basics, version control

Final Thoughts (From One Developer to Another)

If Git feels confusing right now, that’s okay.
It felt that way for all of us.

You don’t need to master everything.
You just need to get comfortable with the basics and trust that clarity comes with use.

Git commands aren’t about perfection.
They’re about progress, history, and helping software engineers learn without losing their work 💻

Take your time.
Make mistakes.
Commit thoughtfully.

Wishing you clean commits, fewer conflicts, and confidence in your Git journey, friends 💙.

Thanks for reading! 🙏🏻
I hope you found this useful ✅
Please react and follow for more 😍
Made with 💙 by Hadil Ben Abdallah
LinkedIn GitHub Daily.dev

Bloom update: new HRT tools, emotional support features, and a full medication database

2026-01-20 14:05:14

I’ve been deep in developing Bloom lately, and constantly having new ideas, so this update brings some of the biggest improvements so far.

Bloom

This release strengthens the core tools and adds a brand‑new searchable HRT medication database.

  1. Smarter, more flexible HRT tools

Bloom’s HRT section now reflects how people actually manage their regimens.

Medication regimen templates for patches, injections, pills, and gel

Custom reminders for dose times, patch changes, injections, and labs

Injection site rotation tracker

Lab result logging with charts

Side‑effect tracking

  1. Journaling & emotional support that adapts to your energy

Transition is emotional and nonlinear, so Bloom’s journaling tools stay gentle and low‑pressure.

Dysphoria/euphoria tags

Transition milestone tracking

Guided prompts, including a low‑energy mode for tough days

  1. Privacy & safety features that assume real‑world risks

Bloom is built for people who can’t always be out or safe.

Quick‑exit button

Optional PIN/biometric lock

Discreet mode to rename or hide the app

  1. New: A searchable HRT medication database

You can now open Medication Database from the sidebar (look for the database icon) to browse clear, accessible info on common HRT medications.

What’s included:

15+ medications

Search by name or purpose

Filters: Estrogen, Anti‑Androgen, Progestin, Testosterone, Other

Each entry includes:

Generic & brand names

Primary use

Administration routes

Common dosage ranges

Side effects & warnings

Key considerations

Prescription status

Covers estradiol (all forms), spironolactone, bicalutamide, progesterone, testosterone (injections, gel, patch), Lupron, and more. A medical disclaimer reminds users to consult their providers.

Want to try Bloom or share feedback?

If any of this speaks to you, I’d love for you to check Bloom out and tell me what you think. It’s still evolving, and community feedback genuinely shapes what I build next. Whether you have ideas, critiques, or thoughts — I would love to hear them.

Bloom

Adding Attachments to PDF Documents Using C#

2026-01-20 14:01:36

How to Insert Attachments into PDF Using C#

PDFs are a cornerstone of digital documentation, often serving as final, immutable records. However, there are many scenarios where bundling supplementary information directly within the PDF itself can significantly enhance its utility. Whether it's source code accompanying a technical report, related documents, or multimedia files, the ability to embed attachments programmatically offers immense practical value for developers. This article will guide you through the process of inserting various types of attachments into PDF documents using C#, focusing on two distinct methods: direct embedding and attachment annotations.

Understanding PDF Attachments and Their Role

A PDF attachment, often referred to as an embedded file, is essentially another file (of any format) stored directly within the PDF document's structure. Unlike external links, these files are self-contained, meaning the PDF remains complete and portable even if the original attachment files are moved or deleted from their source location.

Common use cases for embedding files include:

  • Bundling related documents: Including spreadsheets, Word documents, or other PDFs that provide context or detailed data for the main PDF.
  • Providing source code: For technical documentation, embedding code samples directly within the PDF ensures they are always available.
  • Multimedia integration: Embedding audio or video clips (though often linked for larger files) can enrich interactive PDFs.
  • Archiving: Ensuring all relevant data for a specific record is stored in a single, self-sufficient PDF container.

PDFs offer two primary ways to incorporate these embedded files:

  1. Direct Attachment Insertion: The file is embedded within the PDF's internal structure, accessible through PDF reader's attachment panel, but not visually represented on any specific page.
  2. Attachment Annotation Insertion: The embedded file is linked to a visible icon (an annotation) placed on a specific page within the PDF. Clicking this icon opens the embedded file.

These methods provide flexibility depending on whether the attachment needs to be visually highlighted on a page or simply included as supplementary data.

Setting Up Your C# Project for PDF Manipulation

Before we dive into the code, you'll need to set up your C# .NET project to work with PDF documents. For this tutorial, we will be using Spire.PDF for .NET, a robust library for PDF processing.

Step-by-step project setup:

  1. Create a New C# Project: Open Visual Studio and create a new C# Console Application (.NET Core or .NET Framework, both are supported).
  2. Install Spire.PDF via NuGet:
    • Right-click on your project in the Solution Explorer.
    • Select "Manage NuGet Packages...".
    • Go to the "Browse" tab.
    • Search for Spire.PDF.
    • Select Spire.PDF from the search results and click "Install".
    • Accept the license agreement.

Once installed, you'll typically start your C# file with the necessary using statements:

using Spire.Pdf;
using Spire.Pdf.Attachments;
using Spire.Pdf.Annotations;
using System.IO;
using System.Drawing; // For RectangleF and Color

This setup provides access to all the classes and methods required for PDF manipulation, including handling attachments and annotations.

Directly Embedding Files into a PDF Document

Direct embedding is suitable when you want to include files as part of the PDF's overall structure without tying them to a specific visual element on a page. These attachments are typically accessed through an "Attachments" panel or similar interface within PDF viewer applications.

Let's walk through the process of directly embedding a file into a PDF. We'll use an existing PDF document and embed an image file into it.

Key Steps:

  1. Load or Create a PDF Document: Instantiate a PdfDocument object. You can load an existing PDF or create a new one.
  2. Prepare the Attachment Data: Read the bytes of the file you want to embed.
  3. Create a PdfAttachment Object: Instantiate PdfAttachment, providing the file name.
  4. Set Attachment Properties: Assign the file's byte data, a description, and its MIME type.
  5. Add to Attachments Collection: Add the PdfAttachment object to the doc.Attachments collection.
  6. Save the PDF: Save the modified PDF document.

Here's a complete C# code snippet demonstrating direct attachment insertion:

using Spire.Pdf;
using Spire.Pdf.Attachments;
using System.IO;

namespace PdfAttachmentTutorial
{
    class Program
    {
        static void Main(string[] args)
        {
            // Load an existing PDF document
            PdfDocument doc = new PdfDocument();
            doc.LoadFromFile("InputDocument.pdf"); // Use this if you are processing an existing PDF

            // --- Embed a text file ---
            string textFilePath = "AttachedText.txt";
            File.WriteAllText(textFilePath, "This is some sample text content for the embedded file.");

            PdfAttachment textAttachment = new PdfAttachment(textFilePath);
            textAttachment.Data = File.ReadAllBytes(textFilePath);
            textAttachment.Description = "Supplemental text information.";
            textAttachment.MimeType = "text/plain";
            doc.Attachments.Add(textAttachment);

            // --- Embed an image file ---
            // For demonstration, let's assume you have an image file named "sample_image.png"
            // You might need to create a dummy image file or use an existing one.
            // Example: byte[] imageData = File.ReadAllBytes("path/to/your/image.png");
            // If you don't have one, this part will throw an error unless you create a dummy file.
            // For this example, we'll assume a dummy image exists.
            string imageFilePath = "sample_image.png";
            // In a real scenario, ensure this file exists or create it.
            // Example: System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(100, 100); bmp.Save(imageFilePath);

            // Let's create a dummy image for the example if it doesn't exist
            if (!File.Exists(imageFilePath))
            {
                using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(100, 100))
                {
                    using (System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(bmp))
                    {
                        graphics.FillRectangle(System.Drawing.Brushes.Blue, 0, 0, 100, 100);
                        graphics.DrawString("Dummy", new System.Drawing.Font("Arial", 12), System.Drawing.Brushes.White, 10, 40);
                    }
                    bmp.Save(imageFilePath, System.Drawing.Imaging.ImageFormat.Png);
                }
            }

            PdfAttachment imageAttachment = new PdfAttachment(imageFilePath);
            imageAttachment.Data = File.ReadAllBytes(imageFilePath);
            imageAttachment.Description = "A supporting image for the document.";
            imageAttachment.MimeType = "image/png";
            doc.Attachments.Add(imageAttachment);

            // Save the modified PDF
            doc.SaveToFile("PdfWithDirectAttachments.pdf");
            doc.Close();

            Console.WriteLine("PDF with direct attachments created successfully!");
        }
    }
}

Preview of the PDF:

Insert Attachments to PDF with C#

Spire.PDF Methods for Direct Embedding:

Method/Property Description
PdfDocument Represents a PDF document. Used to load, create, and save PDFs.
PdfAttachment Represents an embedded file within the PDF.
PdfAttachment(string) Constructor to create an attachment with its file name.
Data byte[] property to set the content of the attached file.
Description string property for a user-friendly description of the attachment.
MimeType string property to specify the MIME type (e.g., "text/plain", "image/png").
doc.Attachments.Add() Adds a PdfAttachment object to the document's collection of attachments.
doc.SaveToFile() Saves the PDF document to a specified file path.

After running this code, open PdfWithDirectAttachments.pdf in a PDF viewer, and you should find the "AttachedText.txt" and "sample_image.png" files listed in the attachments panel.

Adding Attachment Annotations to PDF Pages

Attachment annotations provide a visual cue on a specific page of the PDF, indicating the presence of an embedded file. When a user clicks this icon, the associated embedded file is opened. This is particularly useful for highlighting supplementary content directly related to a section of the document.

Let's enhance our previous example by adding an attachment annotation to the first page of the PDF.

Key Steps:

  1. Load or Create a PDF Document: As before, instantiate a PdfDocument.
  2. Get a Specific Page: Access the desired page using doc.Pages[index].
  3. Prepare the Attachment: Create a PdfAttachment object (you can use an already embedded one or a new one).
  4. Define Annotation Bounds: Specify the RectangleF for the icon's position and size on the page.
  5. Create PdfAttachmentAnnotation: Instantiate this class, linking it to the PdfAttachment and defining its appearance.
  6. Add to Page Annotations: Add the PdfAttachmentAnnotation to the page.Annotations collection.
  7. Save the PDF: Save the modified PDF.
using Spire.Pdf;
using Spire.Pdf.Attachments;
using Spire.Pdf.Annotations;
using System.IO;
using System.Drawing; // For RectangleF and Color

namespace PdfAttachmentTutorial
{
    class Program
    {
        static void Main(string[] args)
        {
            // Load an existing PDF document
            PdfDocument doc = new PdfDocument();
            doc.LoadFromFile("InputDocument.pdf"); // Ensure InputDocument.pdf exists

            // Get the first page
            PdfPageBase page = doc.Pages[0];

            // --- First, embed a file (if not already embedded) ---
            string documentFilePath = "Sample.docx";
            // Create a dummy docx file for demonstration
            if (!File.Exists(documentFilePath))
            {
                using (StreamWriter writer = new StreamWriter(documentFilePath))
                {
                    writer.WriteLine("This is a dummy Word document content.");
                    writer.WriteLine("It's attached as an annotation.");
                }
            }

            PdfAttachment docxAttachment = new PdfAttachment(documentFilePath);
            docxAttachment.Data = File.ReadAllBytes(documentFilePath);
            docxAttachment.Description = "Detailed supplementary document.";
            docxAttachment.MimeType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";

            // --- Now, create an attachment annotation on the page ---
            // Define bounds for the annotation icon (X, Y, Width, Height)
            RectangleF bounds = new RectangleF(50, 100, 30, 30); // Position and size of the icon

            // Create the attachment annotation, associating it with the embedded file
            PdfAttachmentAnnotation annotation = new PdfAttachmentAnnotation(bounds, docxAttachment.FileName, docxAttachment.Data);

            // Set annotation properties
            annotation.Text = "Click to open supplementary document"; // Tooltip text
            annotation.Color = Color.Blue; // Color of the icon
            annotation.Icon = PdfAttachmentIcon.Paperclip; // Icon type (e.g., Paperclip, Pushpin, Graph)

            // Add the annotation to the page
            page.Annotations.Add(annotation);

            // Save the modified PDF
            doc.SaveToFile("PdfWithAttachmentAnnotation.pdf");
            doc.Close();

            Console.WriteLine("PDF with attachment annotation created successfully!");
        }
    }
}

Preview of the PDF:

Insert Attachment Annotation to PDF with C#

Spire.PDF Methods for Attachment Annotations:

Method/Property Description
PdfPageBase Represents a single page in the PDF document.
PdfAttachmentAnnotation Represents a visual icon on a page linked to an embedded file.
PdfAttachmentAnnotation(RectangleF, PdfAttachment) Constructor to create an annotation, specifying its bounds and linked attachment.
Text string property for the tooltip text displayed when hovering over the icon.
Color Color property to set the color of the annotation icon.
Icon PdfAttachmentIcon enum to choose the icon's visual style (e.g., Paperclip, Pushpin).
page.Annotations.Add() Adds a PdfAttachmentAnnotation object to the page's collection of annotations.

When you open PdfWithAttachmentAnnotation.pdf, you'll see a blue paperclip icon at the specified coordinates (50, 100) on the first page. Clicking this icon will open the "SupplementaryDoc.docx" file that was embedded.

Conclusion

The ability to programmatically insert attachments into PDF documents using C# offers a powerful way to create richer, more self-contained, and interactive documentation. Whether you need to bundle related files discreetly within the PDF's structure or visually highlight supplementary content on a specific page, Spire.PDF for .NET provides intuitive and effective tools for both direct attachment insertion and attachment annotation insertion.

By following the steps and code examples provided, you can integrate these capabilities into your C# applications, enhancing document processing workflows and delivering more comprehensive PDF outputs. Experiment with different file types, icon styles, and attachment descriptions to fully leverage the flexibility offered by these techniques. Further exploration of the Spire.PDF documentation will uncover even more advanced possibilities for PDF manipulation.

Ringer Movies: ‘Just One of the Guys’ With Bill Simmons, Kyle Brandt, and Joanna Robinson | The Rewatchables

2026-01-20 14:01:20

Join Bill Simmons, Kyle Brandt, and Joanna Robinson on 'The Rewatchables' as they dive headfirst into the 1985 teen comedy 'Just One of the Guys.' They're practically pursuing a journalism career just by revisiting this flick, starring Joyce Hyser, Clayton Rohner, and Billy Jayne.

Watch on YouTube

Building a minimal Go framework in public (v0.1.3)

2026-01-20 13:58:59

What happens when you build a web framework with one simple rule: zero dependencies? That's the question behind Marten, a minimal HTTP framework built entirely on Go's standard library.

The Philosophy

Most Go web frameworks pull in dozens of dependencies. Gin has 9 direct dependencies. Echo has 11. Fiber has 15. Each dependency brings its own dependencies, and suddenly your go.mod looks like a phone book.

Marten takes a different approach: use only what Go gives you. No external packages. No vendor lock-in. Just net/http, encoding/json, and the rest of the standard library.

What It Looks Like

Here's a complete API in Marten:

package main

import (
    "github.com/gomarten/marten"
    "github.com/gomarten/marten/middleware"
)

func main() {
    app := marten.New()

    app.Use(middleware.Logger)
    app.Use(middleware.Recover)

    app.GET("/", func(c *marten.Ctx) error {
        return c.OK(marten.M{"message": "Hello, World!"})
    })

    app.GET("/users/:id", func(c *marten.Ctx) error {
        id := c.ParamInt("id")
        return c.OK(marten.M{"id": id})
    })

    app.Run(":8080")
}

Clean. Familiar. No magic.

The Core Features

Fast Routing with Radix Trees

Marten uses a radix tree router for efficient path matching. It handles path parameters (:id), wildcards (*filepath), and route groups:

api := app.Group("/api/v1")
api.GET("/users", listUsers)
api.GET("/users/:id", getUser)
api.POST("/users", createUser)

Middleware That Makes Sense

Middleware in Marten is just a function that wraps a handler:

func Timer(next marten.Handler) marten.Handler {
    return func(c *marten.Ctx) error {
        start := time.Now()
        err := next(c)
        log.Printf("took %v", time.Since(start))
        return err
    }
}

The framework includes 14 built-in middleware: Logger, Recover, CORS, RateLimit, BasicAuth, Timeout, Secure, BodyLimit, Compress, ETag, RequestID, Static, and NoCache.

Context Pooling

Every request gets a Ctx object from a sync.Pool. This reduces allocations and keeps memory usage low, even under heavy load:

func handler(c *marten.Ctx) error {
    // Path parameters
    id := c.Param("id")

    // Query parameters
    page := c.QueryInt("page")

    // JSON binding
    var user User
    c.Bind(&user)

    // Response helpers
    return c.OK(user)
}

Static File Serving

The latest release (v0.1.3) adds static file serving with all the features you'd expect:

app.Use(middleware.StaticWithConfig(middleware.StaticConfig{
    Root:   "./public",
    Prefix: "/static",
    MaxAge: 3600,
    Browse: false,
}))

Content-type detection, HTTP caching (If-Modified-Since), directory browsing, and security against directory traversal attacks—all built-in.

The Performance Story

How does a zero-dependency framework perform? Surprisingly well.

Benchmarks against Gin, Echo, and Chi show Marten holding its own:

Benchmark Marten Gin Echo Chi
Static Route 1464 ns/op 1336 ns/op 1436 ns/op 2202 ns/op
Param Route 1564 ns/op 1418 ns/op 1472 ns/op 2559 ns/op
JSON Response 1755 ns/op 2050 ns/op 1835 ns/op 1868 ns/op

Not the fastest, but competitive. And with zero dependencies.

Real-World Use Cases

Microservices

When you're building dozens of microservices, dependency bloat adds up. Marten keeps your Docker images small and your build times fast.

REST APIs

Build production-ready APIs with built-in middleware for logging, rate limiting, CORS, and authentication:

app := marten.New()

app.Use(
    middleware.RequestID,
    middleware.Logger,
    middleware.Recover,
    middleware.CORS(middleware.DefaultCORSConfig()),
    middleware.RateLimit(middleware.RateLimitConfig{
        Max:    100,
        Window: time.Minute,
    }),
)

api := app.Group("/api/v1")
api.GET("/users", listUsers)
api.POST("/users", createUser, authMiddleware)

Single Page Applications

Serve your SPA with automatic fallback to index.html for client-side routing:

// API routes
app.GET("/api/users", listUsers)

// Serve static files
app.Use(middleware.Static("./dist"))

// SPA fallback
app.NotFound(func(c *marten.Ctx) error {
    if strings.HasPrefix(c.Path(), "/api/") {
        return c.NotFound("endpoint not found")
    }
    // Serve index.html for client-side routing
    return c.HTML(200, indexHTML)
})

Learning and Teaching

Want to understand how web frameworks work? Read Marten's source. It's ~2,000 lines of readable Go code. No abstractions hiding abstractions.

The Testing Story

Marten v0.1.3 ships with 325 tests covering:

  • Unit tests for every component
  • Integration tests for real-world workflows
  • Stress tests with 1,000+ concurrent requests
  • Edge cases and error conditions

All tests pass with Go's race detector. No known memory leaks. Production-ready.

What's Missing (And Why)

Marten doesn't have:

  • ORM integration: Use database/sql directly
  • Template engine: Use html/template from stdlib
  • Validation library: Write your own or use a third-party package
  • WebSocket support: Coming in a future release

The philosophy is simple: if the standard library can do it, use the standard library. If you need more, add it yourself.

The Roadmap

Future releases will add:

  • WebSocket middleware
  • Template rendering helpers
  • Session management middleware
  • Enhanced static file serving options

But always with the same constraint: zero dependencies.

Try It Yourself

go get github.com/gomarten/[email protected]

Check out the examples for CRUD APIs, JWT auth, file servers, and more.

The Takeaway

Marten isn't trying to replace Gin or Echo. It's an experiment in minimalism. A proof that you can build a capable web framework without pulling in the world.

Sometimes, less is more.

Links: