random thoughts

Rewrite in Go

· Rafał

I did a useless experiment and decided to rewrite a pretty standard Rails app in Go.

Obviously not me directly, I just had my clankers do it. Did I learn anything? Not really. Was it worth it? Probably not.

But first, some history.

The app I’m referring to is almost-dead, mostly visited by bots and spammers, it’s a service about Quake. It has some old content, some forums, and once every few months somebody will post a ‘hey guys, are you still alive?’ comment which will get 1 response after maybe a few weeks. It’s fascinating to observe. I even got sentimental and wrote some news after the migration.

I still keep it because it was my first ‘real’ web app written in Ruby on Rails. Plus I wasted spent my youth there as a kid arguing with strangers on the forums, posting stuff and whatnot. It’s mostly a CRUD, but unfortunately when you look closer you will see a myriad of functionalities buried underneath - permissions with roles, versioning, moderation panels, preferences, in-app notifications, file upload and file post-processing, a few background workers etc. Mostly basic stuff - but it does add up in terms of lines of code.

Obviously I would never rewrite it myself - I had that idea a few times, but given the amount of effort - why bother? But nowadays, armed with clankers in the form of codex, opencode, claude and a myriad of LLM models I could just do it.

So slowly, over two weeks I did that after hours, because - why not? After all I had some spare allowance to burn tokens in a few subscriptions.

Slopcoding at its finest

So what are the learnings here?

There are no learnings, really. Figuratively and literally. Because - as said - scope adds up when you look at the app as a whole, and tracking any progress gets mentally taxing pretty quickly.

With such a rewrite you’re probably gonna tell your agents to track progress somewhere, but despite that things will fall through the cracks and I don’t see how you can not correct the course during the process; one wrongly interpreted sentence and agents will start building some really crazy (and totally useless) functionalities or abstractions; then again I didn’t spend weeks reviewing the perfect migration plan.

There is also no learning from a human perspective - because code grows so fast it’s obviously getting impossible to track or build any mental model of it, so it all feels alien and I found myself asking the fundamental question of how things were constructed, which is not a great feeling.

However, it’s amazing such things are even possible nowadays and the app mostly worked - I had to do a few follow-up sessions to fix broken things, do security-related improvements and port things that the llm forgot (gotta love context rot) - but the fact is I would never be able to do it myself given time constraints.

Indeed I feel like software economics will change, unless of course tokens start costing us actual money and we’ll be back to replacing expensive LLMs with cheaper junior developers, lol. At the same time I wonder who’s gonna support all those machine generated lines of code generated at massive scale nowadays.

As a bonus - some useless stats from scc:

scc rails/
───────────────────────────────────────────────────────────────────────────────
Language            Files       Lines    Blanks  Comments       Code Complexity
───────────────────────────────────────────────────────────────────────────────
Ruby                  667      42,902     7,199     7,350     28,353        838
Sass                   29       4,534       533       314      3,687          0
Ruby HTML              28         866       109         0        757         80
YAML                   22       1,619       104        33      1,482          0
Markdown               16       2,663       684         0      1,979          0
JavaScript             15       2,301       160       391      1,750        297
CoffeeScript           11       1,182       128       376        678         64
Plain Text              8         890       155         0        735          0
Gemfile                 7         294        39        91        164          0
Rakefile                7         584        65        85        434         18
HTML                    6         344        46        13        285          0
BASH                    5          24         7         5         12          3
CSS                     4         319        29        27        263          0
JSON                    4      11,837         0         0     11,837          0
Dockerfile              1          68        10         0         58         10
License                 1          21         4         0         17          0
SQL                     1       1,187         3         0      1,184          0
SVG                     1         438         0         0        438          0
Shell                   1          18         2         4         12          2
XML                     1       6,899         0         1      6,898          0
───────────────────────────────────────────────────────────────────────────────
Total                 835      78,990     9,277     8,690     61,023      1,312
───────────────────────────────────────────────────────────────────────────────

scc go/
───────────────────────────────────────────────────────────────────────────────
Language            Files       Lines    Blanks  Comments       Code Complexity
───────────────────────────────────────────────────────────────────────────────
Go                    471     107,544    10,464    12,060     85,020     10,166
HTML                   52         439        54        13        372          0
SQL                    37       5,656       547     1,661      3,448          8
Sass                   31       4,719       543       364      3,812          0
JavaScript             24       3,976       230       560      3,186        412
Markdown               23          41         4         0         37          0
Templ                  22       1,324        69       193      1,062         63
YAML                   11       1,166        35        39      1,092          0
XML                     4          34         0         0         34          0
CSS                     3         243        18        27        198          0
JSON                    2          27         0         0         27          0
Ruby                    2          87        10        29         48          0
Docker ignore           1           6         0         0          6          0
Dockerfile              1          60        14        18         28          1
Plain Text              1          19         4         0         15          0
SVG                     1         438         0         0        438          0
───────────────────────────────────────────────────────────────────────────────
Total                 686     125,779    11,992    14,964     98,823     10,650
───────────────────────────────────────────────────────────────────────────────

Not very interesting numbers due to the sheer difference between stacks (a lot of rails code was just hidden in gems). If you drop sqlc queries/generated code and generated templates, exclude vendored gems and vendored js libs in both apps, some helper bash/ruby scripts and so on - you would see something like:

┌────────────────────┬───────┬────────────┐
│                    │ Files │ Code lines │
├────────────────────┼───────┼────────────┤
│ Go app (go/)       │ 603   │ 74,481     │
├────────────────────┼───────┼────────────┤
│ Rails app (rails/) │ 625   │ 23,778     │
└────────────────────┴───────┴────────────┘

The only problem is that I can somewhat reason behind this Rails code from 2011, but I have mostly no idea what’s going on in the Go port. But who cares, right? If it works, it works?

So the natural next step would be rewrite in Rust, I guess?