跪拜 Guibai
← Back to the summary

A Frontend Dev Inherits a Go Backend and Ships Features with AI-Generated Code

Recently, I learned that the backend developer I usually work with is leaving, and the company has no plans to hire a replacement for now.

So, my department head came to me and asked me to take over the backend work.

On the surface, I expressed regret about the backend developer's departure, but deep down, I was quite happy.

For one, I had wanted to learn a backend language for a long time; secondly, the company isn't very busy right now, so the pressure isn't too high.

For a frontend developer, backend-related knowledge shouldn't be too unfamiliar, and many frontend developers have transitioned to full-stack or "big frontend" roles.

Moreover, with the rise of AI in the last two years, many companies are cutting costs and increasing efficiency, which has further accelerated this trend.

I remember doing some work related to a Node.js middle layer a few years ago, so I'm still familiar with simple database CRUD operations and server deployment.

Although the backend project I'm taking over this time is written in Go, I feel that for anyone who has been developing for a few years, quickly getting familiar with a new language isn't a difficult task, especially now with the help of AI.

To Refactor or Not?

I remember at the beginning, I discovered that a competitor's community, including Juejin, was implemented using Nuxt, which seems beneficial for SSR (Server-Side Rendering). So, I considered refactoring with Nuxt.

But before I could even start, I heard from my department head that the community needs to go live by the end of August. Considering the amount of work still to be done and that part of the current backend service is still used by the client-side, I had to put that idea on hold for now.

Advantages and Disadvantages of Go as a Backend Service

Advantages:

  1. Simple syntax, low learning curve

Go has only 25 keywords, eliminating inheritance, overloading, generics (which were missing early on, added in 1.18), and complex object-oriented programming; the code style is unified (gofmt enforces formatting), so team code styles won't become messy.

  1. Simple deployment

Direct static compilation outputs a single binary file, no JVM, no dependency packages; you only need to transfer one file to the server to run it, and cross-platform cross-compilation is easy.

  1. Lightweight, supports high concurrency

goroutines are scheduled by the runtime, with an initial stack of only 2KB, making memory usage controllable for millions of concurrent coroutines; paired with channels for communication, concurrent code is concise without tedious thread pool configurations.

  1. Native support for the cloud-native ecosystem

Cloud-native infrastructure like Docker, K8s, etcd, Prometheus, Istio is almost entirely developed in Go; the standard library comes with http, rpc, encryption, and concurrency tools, requiring few third-party frameworks.

Disadvantages:

Compared to traditional backend services like Java, Go's capabilities are not yet rich enough, making it unsuitable for large, complex enterprise-level systems.

Taking Over the Backend Project

The first thing I did when taking over the Go backend project was to understand how the backend service starts and deploys, as well as the configuration for connecting various cloud services on AWS, organizing it into the following document.

The second thing was refactoring the backend's code structure. Previously, the project consisted entirely of Go files, with some files exceeding a thousand lines.

Now, I've implemented a standard Go backend service code structure, though it hasn't been separated into controller, service, repository, etc., yet.

The third thing was fixing a few backend bugs to get some practice, all of which I had the AI modify, and they were all fixed in one go, perfect!

Feature Development (AI Coding)

Two days ago, the product manager proposed a message management requirement: first, configure the trigger nodes for corresponding operations in the admin backend, and after frontend community users perform actions like login/registration/likes/favorites, push a message to the corresponding user.

This requirement isn't too difficult; the main task is to dynamically generate message content based on the configured trigger templates at the relevant operation interfaces and then push it to the corresponding user.

But I can't even "write" frontend code anymore, let alone backend.

So, I skillfully fed the requirement description and prototype diagrams to Codex, letting it first organize a development plan and points to note.

A few minutes later, Codex produced a detailed development plan and the database tables that needed to be created.

Since I currently need to manually execute database table creation in the database, I had it directly generate the SQL execution script for my convenience.

As for the subsequent development steps, I handed them all over to the AI.

After the AI finished developing the backend code, I would first test the corresponding interfaces in Postman. Generally, there were no issues, and afterward, I could directly have the AI generate documentation for the frontend to integrate with.

SSE (Server-Sent Events) for Real-time Server Push

This requirement also involved a real-time message push feature, meaning the frontend message count needed to sync in real time.

Initially, the only solutions I could think of were WebSocket and frontend polling, but I thought WebSocket was more troublesome, and while I had written frontend polling many times before, a high polling frequency would result in a messy console full of requests, which didn't look good.

So, I first asked the AI for a recommended solution, and the AI recommended SSE (Server-Sent Events).

This is a unidirectional, long-connection, real-time communication Web standard (HTML5 specification). Simply put: the browser initiates a single HTTP request, and the server continuously pushes data to the client. Throughout the process, only the server sends messages; the client cannot send data.

Brilliant, it perfectly met my needs. Not only is it lighter than WS, but it also only sends one HTTP request.

So, I bossed Codex around again, and it was implemented in less than two minutes.

It feels like with AI Coding now, I've become omnipotent, haha!

Below is a brief introduction to SSE:

I. What is it

SSE, which stands for Server-Sent Events, is a set of unidirectional real-time communication Web standards (HTML5 specification).

Simply put: the browser initiates an HTTP request, and the server continuously pushes data to the client. Throughout the process, only the server sends messages; the client cannot send data.

The biggest difference from WebSocket:

II. Core Principles

  1. The client initiates a GET request using the browser's built-in EventSource API;
  2. The server response header has a special identifier telling the browser this is an SSE long connection:
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
  1. The connection is not closed, and the server continuously writes data in a fixed format in segments;
  2. The browser automatically parses the data stream and triggers the onmessage event to get the message;
  3. If the network disconnects, EventSource automatically reconnects (built-in disconnection recovery).

III. Fixed Server Message Format

Each message consists of multiple lines of fields, a blank line signifies the end of a message

data: message content
event: custom event name (optional)
id: message ID (used for resending after disconnection recovery)
retry: reconnection interval in milliseconds (optional)

Example:

data: {"count":"3"}

IV. Pros and Cons Comparison

Pros

  1. Based on native HTTP, no extra protocol needed, Nginx/Apache have natural support, no protocol upgrade required;
  2. Browser-native EventSource, no need for third-party JS libraries;
  3. Built-in automatic disconnection recovery, message ID-based breakpoint resumption;
  4. Lightweight, suitable for display-only real-time scenarios, lower overhead than WebSocket;
  5. Supports standard CORS for cross-origin requests.

Cons

  1. Unidirectional communication, the client cannot send data to the server;
  2. Can only use GET requests, does not support POST;
  3. Poor compatibility with some older browsers (all modern browsers support it);
  4. Subject to the browser's maximum concurrent connection limit (up to 6 SSE long connections per domain).

V. Applicable Scenarios

Scenarios where only the server actively pushes and the frontend only receives:

  1. Real-time data dashboards (monitoring metrics, order statistics, inventory refreshes)
  2. Message notifications, system announcements, in-site alerts
  3. Real-time log printing, AI streaming output (ChatGPT/Wenxin Yiyan typewriter effect)
  4. Real-time stock, market, sensor value refreshes
  5. Progress push (file export, task execution progress)

VI. Typical Code Examples

  1. Frontend JS (EventSource)
// Establish SSE connection
const sse = new EventSource("/api/sse/stream");
// Receive normal messages
sse.onmessage = (e) => {
  console.log("Received push:", JSON.parse(e.data));
};
// Listen for custom event events
sse.addEventListener("notice", (e) => {
  alert("System notification:" + e.data);
});
// Connection error, disconnect auto-reconnect
sse.onerror = () => {
  console.log("Connection abnormal, auto-retrying");
};
// Manually close the connection
// sse.close();
  1. Go Backend Minimal Example
func SSEStream(w http.ResponseWriter, r *http.Request) {
    // Set SSE required response headers
    w.Header().Set("Content-Type", "text/event-stream")
    w.Header().Set("Cache-Control", "no-cache")
    w.Header().Set("Connection", "keep-alive")
    flusher, ok := w.(http.Flusher)
    if !ok {
        http.Error(w, "Streaming not supported", 500)
        return
    }
    // Continuous loop push
    tick := time.Tick(2 * time.Second)
    for range tick {
        msg := `data: {"time":"` + time.Now().Format(time.RFC3339) + `"}\n\n`
        _, _ = w.Write([]byte(msg))
        flusher.Flush() // Force flush to TCP stream, no caching
    }
}

VII. How to Choose Between SSE and WebSocket?

  1. Only server sends data down, frontend does not upload data → SSE (simple, stable, easy to deploy) Such as: dashboards, AI streaming output, real-time statistics, notifications
  2. Frontend and backend need to send and receive messages mutually → WebSocket Such as: chat, online collaboration, real-time battles, bidirectional interaction

VIII. Backend Development Notes

  1. Must enable Response stream flushing (Flush), otherwise messages will accumulate in the buffer;
  2. Long connections will occupy server-side coroutines/threads, connection management and timeout cleanup are needed for high-concurrency scenarios;
  3. Disconnection recovery should cooperate with the Last-Event-ID request header to implement message resending;
  4. When using Nginx as a reverse proxy, buffering must be turned off: proxy_buffering off;, otherwise push will be delayed.

Supplement: Why AI Streaming Output Heavily Uses SSE

The large language model typewriter effect (outputting character by character) does not require the frontend to send messages; the server returns text in segments. SSE is lightweight and simple, without needing to maintain a WS connection state. Nowadays, almost all conversational AI interfaces implement streaming returns based on SSE.