What Happens When You Make the Same Software on GO + Htmx vs JS (Express JS)?

kafka franzkafka franz
5 min read

The Origin of the Discussion

A recent conversation with a friend sparked this deep dive into programming languages. Our debate centered on programming languages and their impact on the development cycle. While technical discussions often revolve around performance, low-level versus high-level characteristics, we realized we hadn't truly understood the nuanced experience of programming in different languages within production-class systems.

Experimental Framework

Goal

Evaluate the developer experience of different frameworks using a comprehensive, all-rounded project.

Project Selection: Video Conferencing Application

Rationale for Chosen Project

  • Elaborate full-stack project involving complex technologies

  • Requires handling:

    • HTTP protocols

    • WebSockets

    • WebRTC

    • Concurrency management

  • Demonstrates pros and cons of different frameworks

  • Balances feature complexity with performance requirements

Library Selection Criteria

Libraries were carefully chosen based on:

  • Most comprehensive tutorials

  • Most starred/used GitHub repositories

  • Consultation with Large Language Models (LLMs)

Phased Project Approach

The project was methodically structured into five progressive stages:

  1. Chatroom with WebSockets

  2. Video Conferencing Implementation

  3. Users and Multiple Rooms Support

  4. Authentication and Database Integration

  5. Comprehensive Testing

Hypothesis: This approach covers most commonly used web development utilities and provides a robust comparison ground.

Deep Dive: WebSocket Chatroom Implementation

Architectural Considerations

Golang Implementation

Hub Structure

type Hub struct {

sync.RWMutex

clients map[*Client]bool

broadcast chan *Message

register chan *Client

unregister chan *Client

messages []*Message

}

Channel and Concurrency Characteristics
  • Channels manage events through unique behavioral patterns

  • sync.RWMutex provides thread-safe operations

  • Explicit handling of client connections and message propagation

Data Flow Mechanism
  1. Main Server (Hub) Initialization:

    • register channel: Client connection registration

    • unregister channel: Client disconnection management

    • broadcast channel: Message distribution to all clients

  2. Client Goroutine Operations:

    • Read Goroutine:

      • Captures WebSocket messages

      • Decodes incoming messages

      • Forwards decoded messages to Hub via broadcast channel

    • Write Goroutine:

      • Monitors client's send channel

      • Writes messages to WebSocket connection

      • Sends periodic ping messages to maintain connection

  3. Message Propagation Workflow:

    • Client message → Read goroutine → Hub broadcast channel → Write goroutine → Client WebSocket

JavaScript Implementation

Socket.IO Selection Rationale
  • Preferred over raw WebSockets

  • More prevalent in production environments

  • Provides robust connection management

Code Architecture
  • Divided into two primary components:

    1. Public-side (client browser)

    2. Server-side code

High-Level Communication Flow
  1. Server Setup:

    • Listens for incoming WebSocket connections

    • Handles message broadcasting

    • Manages client interactions

  2. Client Setup:

    • Connects with authentication details

    • Receives welcome messages

    • Sends and receives messages

Detailed Client-Server Interaction
  1. Initial Connection:

    • Client connects to server

    • Server sends welcome message

  2. Connection Acknowledgment:

    • Client responds with "hello all" message
  3. Message Transmission:

    • Client sends message to server

    • Server broadcasts to all connected clients

Comparative Analysis and Insights

Abstraction Levels

JavaScript Approach:

  • High-level abstractions

  • Implementation details obscured

  • Enables focus on feature development

  • Quick prototyping capabilities

Golang Approach:

  • Mid-level language exposure

  • Explicit system-level control

  • Concurrency responsibility placed on developer

  • Enables development of performant systems

Development Prerequisites

JavaScript:

  • Lower technical entry barrier

  • Can create functional applications quickly

  • Relies heavily on library abstractions

  • Minimal understanding of underlying mechanisms required

Golang:

  • Higher technical prerequisites

  • Requires deeper understanding of:

    • WebSocket protocols

    • Concurrency management

    • System-level interactions

  • More explicit control over implementation

Philosophical Paradigms: Backend vs Frontend Approaches

Go + HTMX: Backend Engineer's Frontend

Go + HTMX represents a paradigm shift where backend engineers reclaim frontend development:

  • Server-Side Rendering (SSR):

    • Computation happens on the server

    • Minimal client-side JavaScript

    • HTML returned as the primary interface

    • Backend logic drives user interactions

  • Developer Experience:

    • Backend engineers feel more comfortable

    • Direct control over application flow

    • Performance-optimized rendering

    • Reduced client-side complexity

  • Architectural Characteristics:

    • Strongly typed

    • Explicit data flow

    • Predictable state management

    • Lightweight client-side footprint

JavaScript: Frontend's Backend

JavaScript, conversely, represents a frontend-driven backend approach:

  • Client-Side Dominance:

    • Complex logic runs in the browser

    • Heavy client-side computations

    • Dynamic, interactive experiences

    • Backend becomes primarily a data service

  • Developer Experience:

    • Frontend developers extend backend capabilities

    • Rich, interactive user interfaces

    • Flexible state management

    • Rapid prototyping

  • Architectural Characteristics:

    • Dynamic typing

    • Event-driven architecture

    • Extensive ecosystem

    • Complex client-side state management

The Philosophical Divide

Go + HTMX says: "The server knows best, clients are thin." JavaScript says: "The client is king, backend serves its needs."

This fundamental philosophical difference isn't about technical superiority, but about different problem-solving approaches and development perspectives.

Architectural Philosophies

Go + HTMX: Backend Engineer's Frontend

  • Server-side rendering primacy

  • Minimal JavaScript requirements

  • Performance-optimized architecture

  • Direct application flow control

JavaScript: Frontend's Backend

  • Rich ecosystem of libraries

  • Event-driven architecture

  • Extensive client-side capabilities

  • Rapid feature implementation

Personal Insights and Motivations

Why This Project Over Reading?

Theoretical understanding has limitations. This hands-on exploration revealed nuances impossible to capture through passive reading:

  • Practical implementation challenges

  • Real-world design decision impacts

  • Tangible differences in language philosophies

Personal Developmental Wins

  • Enhanced language design understanding

  • Improved context-switching skills

  • Practice with design pattern variations

  • Exploration of new development workflows

  • Hands-on experience with Neovim and Lazygit

Concluding Thoughts

Go and JavaScript represent fundamentally different problem-solving approaches. Their architectural differences enforce unique developmental paradigms.

The true power lies not in declaring a superior technology, but in understanding each language's strengths and selecting the right tool for specific project requirements.

May your code be elegant, and your debugging be swift! 🚀

0
Subscribe to my newsletter

Read articles from kafka franz directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

kafka franz
kafka franz