I think the reason I love writing websites in Go so much is how simple it is to use, i can just `apt-get install golang-go` or whatever and everything just works, `go build` will give me a binary that I can immediately copy to a server and run in production if I want

I work on a lot of small projects and being able to spend ~0 time setting up my development environment is worth a lot to me

that said there are a lot of things I don't know how to do in Go, for example I don't know how to do database migrations or manage users and I know that those things would be extremely easy in Rails etc

I guess I'd rather have a very simple tool that's less powerful in some ways but that I know will work

(not really looking for advice thanks)

2/?

@b0rk do you feel like the ease of use comes at a cost? Like, aside from the things you don't yet know how to do, is there more complexity being hidden by the tool or more cognitive load you have to carry?

I'm always curious where the "hard" goes when we get "easy".

@katachora hmm not always? for example in general I like using Vue.js more than vanilla js

vue definitely hides more complexity, but also for me managing state in vanilla js involves a lot of cognitive load, and Vue feels overall "simpler" to me to use even though I don't understand everything it's doing and occasionally it's confusing to debug

@b0rk I wanna pick your brain a bit: I've always had a place in my database where the current version is stored (e.g PRAGMA user_version in sqlite), and then had a tiny function which takes in an expected version and runs 'res/sql/migrate-to-%04d.sql' files in transactions to get it up to the right version. This is regardless of whether I'm writing Node, Go or C++. Am I missing anything? I've never used Rails or other "proper" migration systems, so I really don't know what they'd do for me

@mort hmm so in Rails I can type something like

rails generate migration AddPartNumberToProducts part_number:string

and it'll add a `part_number` column to the `products` table in development

and then when you want to run the migrations in production, you can just run `rails migrate` and it'll run the migration

I think it's kind of the same thing but with less typing and more Rails magic, but I haven't thought about migrations very deeply honestly so I can't say what the difference is.

@mort it's cool to hear about your system, appreciate you sharing it!
@b0rk @mort I think one use case of having migrations coded in your app is that if you have to some non-trivial data manipulation because of the database change, you can code it in the migration file. I can’t remember the last time I had to do that, though, but it explains why so many frameworks provide it.

@mort @b0rk That's most of it.

The main difference from "real" Rails migrations is that Rails lets you supply a "down" method which undoes the change. (If you don't, there's black magic which tries to guess what should be there, which... works unless it doesn't.)

A more minor difference is using creation-date timestamps to sequence the scripts instead of an incrementing sequence. This avoids version-number collisions when merging multiple or lines of development, tho not other issues.

@b0rk interesting! I come at this from the opposite direction: I value Ruby's expressive power, and env setup isn't frequent enough to bother me (also I try to document/automate really frustrating bits). When I've poked at Go, it feels tediously low-level *to me*: "Wait, it takes *how* many lines of code to say that? And I have to type the whole thing out every dang time???"

(Not trying to yuck your yum! Genuinely curious about the difference here.)

@geeksam my best guess is that I tend to abandon my projects for a very long time (months/years), so I kind of need to redo the env setup every single time I work on the project. So it doesn't feel like a one-time cost.

for example I was thinking about working on a Rails project that I last worked on in May 2023 today, and for whatever reason I don't have Ruby set up anymore, so I'd have to go redo it from scratch, and I just don't feel like it so I'm going to give up instead

@b0rk yeah, I can see that. Seems kinda like how I've spent so long avoiding JS that every time I think of starting I realize I'd have to shave so many yaks just to figure out how package management works that I'm like "yeah, there's always more backend Ruby work to be done!"

But hey, if you want a pair for the Ruby setup, I'm game. :)

@geeksam haha I appreciate that, I've set up ruby a lot of times and gotten a lot of help over the last 10 years and I think the issue is less that I can't do it and more that I just don't feel like it haha
@b0rk relatable! If you find that having another person to help you focus is useful, hmu. :)
@geeksam @b0rk I felt the same way about Go. It reminded me of Pascal, for some reason.

@geeksam @b0rk you could have both: and expressive language and a single binary if you'd try @CrystalLanguage. 😊

This might be especially interesting if Ruby is your thing.

@b0rk This is not meant as advice, just a tool recommendation if you don‘t know of it: I‘m using dbmate (https://github.com/amacneil/dbmate) for most of my projects where whatever language/framework I use does not bring their own db migration tool. It‘s also written in go, very fast and works everywhere.
GitHub - amacneil/dbmate: 🚀 A lightweight, framework-agnostic database migration tool.

🚀 A lightweight, framework-agnostic database migration tool. - amacneil/dbmate

GitHub
@danjel @b0rk very interesting! Thanks for sharing that — will check it out.
@b0rk The nice thing about tools is that there are many good ones to choose from.
@b0rk I feel exactly the same way and dislike Rails for the same reasons. It’s very nice to know I’m not alone. 🙏🏼
@b0rk The lack of a full-fat (ASP.NET, express.js) web framework in Go is pretty remarkable. It was certainly due to the lack of affordances like generics in the past, but I’m not sure if anyone really wants to work in this space. The frameworks to support writing APIs and hosting a SPA are fine, it’s just the full templated, server-rendered forms frameworks that are…weak.
@b0rk I use https://github.com/golang-migrate/migrate for migrations. Even for some of my non-Go projects. I love how migrations are just pure SQL; no DSLs
GitHub - golang-migrate/migrate: Database migrations. CLI and Golang library.

Database migrations. CLI and Golang library. Contribute to golang-migrate/migrate development by creating an account on GitHub.

GitHub
@b0rk the fascinating question for me is “Can we have both?” To me, it’s a genuine question, and it’s not obvious which way the answer should go.
@b0rk In general, there are trade offs that seem impossible to avoid (low level control of memory vs a super-simple programming model seems like a real “choose one” situation). But stability, ease of deployment and batteries included for web development seems like it might be an achievable combo.

@b0rk We still don't have a good way to dev-en-as-code, do we? 😓

There _are_ options (devcontainer, nix, ...), but for some reason, none seems to really catch on. No critical mass? 🤔

@b0rk do you use Hugo?

@ffflorian I do! (though i was talking about dynamic sites in that post)

I've been using it for 8 years and my main frustration is that it it seems break backwards compatibility a lot so I've actually spent a lot of time figuring out how to compile old versions to build my site. The version I use is from 2018 before go introduced `go.mod` so it's a little more complicated than usual.

@b0rk interesting, thanks 🙂

@b0rk I spent some time learning it, about a decade ago now. I did like the way they chose to incorporate things as standard language features that are usually external parts of the environment.

E.g. the way they short-circuited all holy wars about code formatting, tab indentation, braces etc. by saying "here's a code formatting tool--the default output from that is the standard". And then people could just build it into editing tools and such.

@b0rk At the time, it was a very young language and there were a lot of features it didn't have that it does now, so maybe it's worth a second look.

@b0rk

that's always what i look for. make a change, save and then reload the page to get the latest version.

factoring is everything. you can do it in javascript too but you have to do it for yourself.

@b0rk i agree, tough the recent push for go to automatically download a new toolchain if a dependency requires a newer version of Go.
@b0rk Cool! Do you just edit reverse proxy then to forward http queries from website to the new service?

@AlexPtakhin yes, if I'm running it on a VPS

sometimes the site will be on fly.io or a lambda function and in that case there's no reverse proxy for me to configure

@b0rk has never been documented too much (and it's not working anymore), but I liked a lot go_installer to install it, even better than apt:

```
curl -LO https://get.golang.org/$(uname)/go_installer && \
chmod +x go_installer && \
./go_installer && \
rm go_installer
```

@b0rk this is a really helpful perspective, thank you!
@b0rk That sounds amazing! I thought Python would be the same, but there are always slight issues with the version in distros (e.g. ubuntu 22.04 ships Python 3.8 which does not have tomllib in the stdlib T-T)
@b0rk this is so interesting to me because I use Go so infrequently that every time I come back to it, I struggle with environment setup since how it works Under The Hood isn’t intuitive in my brain yet!
@b0rk I took that approach in 2019 and wrote Evoke for doing this in c++. mkdir src; evoke -d and then edit src/file.cpp, builds and links on file save.

@b0rk as a former python user and now a regular go user, i have to say "go mod init" is a blessing

i am so glad i do not have to futz with GOPATH anymore

@b0rk Given what a nightmare it is to create a production artifact (interpreter+src+dependencies+transformation) for say Node or Python projects, the creating a single executable thing is so nice.

@b0rk is there a simple third step in "respond to HTTP request with the binary you just made" or is it always complicated? forever ago I ran my own PHP stuff on my own server and was satisfied with that but I don't remember it ever feeling simple and I certainly don't remember how to do it anymore

that's just always felt like one of those "and then a miracle happens" steps to me in computing, even though I try to demystify these things for myself

@ChateauErin if you're using your own server, I find that the easiest way is to use Caddy, the config is so much simpler than nginx -- you can just configure something like

mysite.jvns.ca {
reverse_proxy localhost:8333
}

and it'll Just Work (where 8333 is the port the Go server is running on)

I often use something like fly.io instead though, which I find easier to manage

@b0rk oh ok that makes sense, and the program listening on 8333 is as hard or as simple as the language's tools / OS APIs make it. Thanks!

@b0rk Simplicity of installation is underrated!

a) You should check out `uv` for python: eg. https://simonwillison.net/2024/Aug/21/usrbinenv-uv-run/
b) You should check out `hermit` for everything else (except Ruby): https://cashapp.github.io/hermit/

I now use hermit for all my personal projects, for two reasons: it eliminates the "coming back to this project after 5 months; wait, how do I even compile/build/run it?", and I can point people to a repo and they can just clone it and immediately run scripts in it.

#!/usr/bin/env -S uv run

This is a really neat pattern. Start your Python script like this: #!/usr/bin/env -S uv run # /// script # requires-python = ">=3.12" # dependencies = [ # "flask==3.*", # …

@b0rk Oops. Missed the "not looking for advice" on the followup post!

@zellyn no worries, i’m always happy to hear about cool stuff that i hadn’t heard of and that post about uv definitely qualifies

(the reason i say “no advice” is that usually the unsolicited-advice-type replies i get aren’t very interesting haha)

@b0rk In that case, check out Bundlewrap: simple, pythonic alternative to puppet/ansible/etc. Not widely used, but surprisingly nice 🙂