<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michael Stevan Lapandio</title>
    <description>The latest articles on DEV Community by Michael Stevan Lapandio (@kyomel).</description>
    <link>https://dev.to/kyomel</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1172826%2F515ff1cb-4495-4706-8896-cfdb1bbed61b.jpg</url>
      <title>DEV Community: Michael Stevan Lapandio</title>
      <link>https://dev.to/kyomel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kyomel"/>
    <language>en</language>
    <item>
      <title>Graceful Server In Golang</title>
      <dc:creator>Michael Stevan Lapandio</dc:creator>
      <pubDate>Sun, 22 Jun 2025 16:19:23 +0000</pubDate>
      <link>https://dev.to/kyomel/graceful-server-in-golang-36b9</link>
      <guid>https://dev.to/kyomel/graceful-server-in-golang-36b9</guid>
      <description>&lt;p&gt;In this post, we'll walk through how to set up a graceful HTTP server in Golang that handles three crucial life cycle stages: Prestart, Start, and Shutdown. We'll also discuss why handling these life cycles gracefully is important.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;br&gt;
When building servers, it's vital to manage the server lifecycle effectively to ensure reliability and uptime. Graceful shutdowns, in particular, allow your server to finish processing ongoing requests before shutting down, which is critical in production environments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F584rx22onpegf8wyakv8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F584rx22onpegf8wyakv8.png" alt="Server life cycle diagram" width="521" height="724"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will focus on the "Start" step first.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
    "fmt"
    "io"
    "net"
    "net/http"
)

var port = "8080"

type gracefulServer struct {
    httpServer *http.Server
    listener   net.Listener
}

func (server *gracefulServer) start() error {
    listener, err := net.Listen("tcp", server.httpServer.Addr)
    if err != nil {
        return err
    }

    server.listener = listener
    go server.httpServer.Serve(server.listener)
    fmt.Println("Server now listening on " + server.httpServer.Addr)
    return nil
}

func handle(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "Hello student!\n")
}

// Initialize a new server instance and return reference.
func newServer(port string) *gracefulServer {
    mux := http.NewServeMux()
    mux.HandleFunc("/", handle)
    httpServer := &amp;amp;http.Server{Addr: ":" + port, Handler: mux}
    return &amp;amp;gracefulServer{httpServer: httpServer}
}

func main() {
  server := newServer(port)
  server.start()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ve added a &lt;strong&gt;start()&lt;/strong&gt; method on our server type. This allows the instance of &lt;strong&gt;gracefulServer&lt;/strong&gt; returned by our newServer method to have a start life cycle method called on it. We essentially create a listener that can receive TCP communication, and then we spawn a goroutine to monitor incoming requests and serve our response to them. If we run the code given above, we should see that the server starts on port 8080 and prints the same to the output console.&lt;/p&gt;

&lt;p&gt;Well, our listening is happening in a goroutine! The listener has been detached from the main thread. So, technically, when we run &lt;strong&gt;start()&lt;/strong&gt; in our main function, the execution ends and the program quits, closing all open subprocesses.&lt;/p&gt;

&lt;p&gt;To solve this we will need to implement a wait and shutdown properly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shutdown&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
    "flag"
    "io"
    "log"
    "net"
    "net/http"
    "os"
    "os/signal"
    "syscall"
)

type gracefulServer struct {
    httpServer *http.Server
    listener   net.Listener
}

func (server *gracefulServer) start() error {
    listener, err := net.Listen(
        "tcp",
        server.httpServer.Addr,
    )
    if err != nil {
        return err
    }

    server.listener = listener
    go server.httpServer.Serve(server.listener)
    log.Default().Printf("Server now listening on %s\n", server.httpServer.Addr)
    return nil
}

func (s *gracefulServer) shutdown() error {
    if s.listener != nil {
        err := s.listener.Close()
        s.listener = nil
        if err != nil {
            return err
        }
    }

    log.Default().Println("Shutting down server")
    return nil
}

func handle(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "Hello student!\n")
}

func newServer(port string) *gracefulServer {
    mux := http.NewServeMux()
    mux.HandleFunc("/", handle)
    httpServer := &amp;amp;http.Server{Addr: ":" + port, Handler: mux}
    return &amp;amp;gracefulServer{httpServer: httpServer}
}

func main() {

    // This is how you would get command line arguments
    // passed to your binary at runtime.
    // The last parameter in StringVar is actually a 
    // usage help text in case someone messes up.
    var port string
    flag.StringVar(&amp;amp;port, "port", "8080", "./server -port 8080")
    flag.Parse()

    done := make(chan bool, 1)

    // New channel for OS signals
    interrupts := make(chan os.Signal, 1)
    // Set up the channel to be notified on the following signals
    signal.Notify(interrupts, syscall.SIGINT, syscall.SIGTERM)

    server := newServer(port)
    err := server.start()
    if err != nil {
        log.Fatalf("Error starting server - %v\n", err)
    }

    // Goroutine to set up the shutdown flow
    go func() {
        sig := &amp;lt;-interrupts
        log.Default().Printf("Signal intercepted - %v\n", sig)
        server.shutdown()
        done &amp;lt;- true
    }()

    // Wait for the shutdown flow to send true
    &amp;lt;-done
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see we added a new channel to receive one OS signal called &lt;strong&gt;interrupts&lt;/strong&gt;. Next we set up the channel to be notified whenever our OS emits &lt;strong&gt;SIGTERM&lt;/strong&gt; or &lt;strong&gt;SIGINT&lt;/strong&gt;. We created a goroutine to listen to the interrupts channel and gracefully call shutdown. After the shutdown is completed, we emit a boolean to our done channel, allowing the main thread to proceed with the execution, therefore bringing our server life cycle to an elegant close.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prestart&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
    "io"
    "log"
    "net"
    "net/http"
    "os"
    "os/signal"
    "syscall"
)

var welcomeMsg = "Welcome to the graceful server! 💃🏼\n"

type gracefulServer struct {
    httpServer *http.Server
    listener   net.Listener
}

func withSimpleLogger(handler http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Default().Printf("Incoming traffic on route %s", r.URL.Path)
        handler.ServeHTTP(w, r)
    })
}

func (server *gracefulServer) preStart() {
    server.httpServer.Handler = withSimpleLogger(server.httpServer.Handler)
}

func (server *gracefulServer) start() error {
    listener, err := net.Listen(
        "tcp",
        server.httpServer.Addr,
    )
    if err != nil {
        return err
    }

    server.listener = listener
    go server.httpServer.Serve(server.listener)
    log.Default().Printf("Server now listening on %s\n", server.httpServer.Addr)
    return nil
}

func (s *gracefulServer) shutdown() error {
    if s.listener != nil {
        err := s.listener.Close()
        s.listener = nil
        if err != nil {
            return err
        }
    }

    log.Default().Println("Shutting down server")
    return nil
}

func handle(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "Hello student!\n")
}

func greetingHandler(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, welcomeMsg)
}

func newServer(port string) *gracefulServer {
    mux := http.NewServeMux()
    mux.HandleFunc("/", handle)
    mux.HandleFunc("/greeting", greetingHandler)

    httpServer := &amp;amp;http.Server{Addr: ":" + port, Handler: mux}
    return &amp;amp;gracefulServer{httpServer: httpServer}
}

func main() {

    port := "8080"
    done := make(chan bool, 1)
    interrupts := make(chan os.Signal, 1)
    signal.Notify(interrupts, syscall.SIGINT, syscall.SIGTERM)

    server := newServer(port)
    server.preStart()
    err := server.start()
    if err != nil {
        log.Fatalf("Error starting server - %v\n", err)
    }

    go func() {
        sig := &amp;lt;-interrupts
        log.Default().Printf("Signal intercepted - %v\n", sig)
        server.shutdown()
        done &amp;lt;- true
    }()

    &amp;lt;-done
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We added a &lt;strong&gt;preStart()&lt;/strong&gt; method to &lt;strong&gt;gracefulServer&lt;/strong&gt;, just like our other life cycle methods. This one modifies the server’s handler to add another middleware that logs the path of all incoming traffic. Then we invoke this method in our main function before we invoke &lt;strong&gt;start()&lt;/strong&gt;. And there we go! We have successfully added a prestart hook to our server and learned how to add middleware to our routes.&lt;/p&gt;

&lt;p&gt;A graceful server is not just about elegant shutdowns—it's about building reliable, maintainable, and production-ready applications. The patterns we've implemented provide a solid foundation that scales with your application's growth.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A graceful server doesn't just shut down elegantly—it lives elegantly, handling every phase of its lifecycle with purpose and reliability.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Found this helpful? Share it with your fellow Go developers!&lt;/p&gt;

</description>
      <category>go</category>
      <category>backend</category>
      <category>programming</category>
      <category>development</category>
    </item>
    <item>
      <title>Git Switch vs Git Checkout</title>
      <dc:creator>Michael Stevan Lapandio</dc:creator>
      <pubDate>Sun, 15 Jun 2025 17:10:09 +0000</pubDate>
      <link>https://dev.to/kyomel/git-switch-vs-git-checkout-4l50</link>
      <guid>https://dev.to/kyomel/git-switch-vs-git-checkout-4l50</guid>
      <description>&lt;p&gt;If you've used Git for more than a day, &lt;code&gt;git checkout&lt;/code&gt; is probably muscle memory right. &lt;br&gt;
New branch?&lt;br&gt;
&lt;code&gt;git checkout -b feature-x&lt;/code&gt;&lt;br&gt;
Discard file changes?&lt;br&gt;
&lt;code&gt;git checkout -- src/app.js&lt;/code&gt;&lt;br&gt;
Sound familiar right? The problem is the same command handles branches (a high-level task) and files (a low-level task). That overload can lead to mistakes—accidentally nuking changes when you meant to switch branches, or vice versa.&lt;br&gt;
In Git 2.23 (2019), the Git team answered this with two focused tools like &lt;code&gt;git switch&lt;/code&gt; and &lt;code&gt;git restore&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cheat Sheet: Git Checkout vs Git Switch&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fke6c46mygw5enyr6tdwn.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fke6c46mygw5enyr6tdwn.jpeg" alt=" " width="591" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Make the Switch?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clarity: Command names match their purpose—no more guessing.&lt;/li&gt;
&lt;li&gt;Safety: Reduces accidental data loss by separating branch vs. file ops.&lt;/li&gt;
&lt;li&gt;Readability: Cleaner CLI history, easier for teammates to follow.&lt;/li&gt;
&lt;li&gt;Best Practice: Endorsed by Git’s own maintainers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt; &lt;br&gt;
Although it can be hard to break the old &lt;code&gt;git checkout&lt;/code&gt; habit, consistently using &lt;code&gt;git switch&lt;/code&gt; and &lt;code&gt;git restore&lt;/code&gt; will will quickly give you a clearer, safer workflow you’ll soon appreciate.&lt;/p&gt;

</description>
      <category>git</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
