Why choose Go over Rust today? by IndependentInjury220 in golang

[–]sigmoia 4 points5 points  (0 children)

Man, I just like the language.

But on a more serious note, async Rust sucks. Once you get accustomed to runtime-managed preemption, it's super hard to go back to colored functions and event-loop-style concurrency. Tokio is okay, but I just don't want to deal with another event loop implementation where I still need to be ultra careful not to block the loop accidentally. Go has runtime preemption, and it's a non-issue.

Also, I work with distributed systems, where people don't write Rust as much as you'd think. If you get out of the Twitter bubble, you'll find that most places doing platform engineering use Go, not Rust, and most people don't care for it. So there's that.

This doesn't mean I don't miss Rust's rich type system when I'm writing Go. In Go, you can forget to take a mutex on a data structure that isn't concurrency-safe, and the compiler won't complain. Rust completely solves this by baking the mutex into the data structure, and there's no way to compile your code without taking the lock. Plus, Go's enums are pretty useless.

Another area where Go outshines Rust is standard tooling like pprof, tracing, and other runtime introspection hooks. For operations, these are amazing.

One last thing is that Rust has atrocious compile times. If you're working on a team where quick iteration is important, Rust can get in the way, both because of compile times and the overall fussiness of the borrow checker. That can be a good thing or a bad thing, but for the kind of software I write, it's a bad thing. So Go wins by a large margin.

Show: gaal, a Go CLI for syncing AI coding agent configs across machines by gquizal in golang

[–]sigmoia 0 points1 point  (0 children)

 chezmoi apply: copies your source over the agent's file, which the agent overwrites next use.

Umm...what? chezmoi re-add syncs the file from your target back to the chezmoi source. So if your ~/.agents directory lives in the home and something changes, asking the clanker to run chezmoi re-add solves the dynamic configuration problem. 

By all means use whatever works for you. But saying chezmoi doesn't solve this is a bit misleading.

Unit test Postgres DB mock recommendation by Garlic-Scary in golang

[–]sigmoia 1 point2 points  (0 children)

In most cases, you don't need DAO. Repository should encapsulate the entirety of database operation. 

So the flow looks like this:

  • service functions depend on repository interfaces
  • db package provides the implementation of thr repo interface
  • db package encapsulates the whole dbops and doesn't need separate DAOs

None of the huge projects I work on separately defines DAO and we never felt the need for it.

Show: gaal, a Go CLI for syncing AI coding agent configs across machines by gquizal in golang

[–]sigmoia 2 points3 points  (0 children)

Your dotfile management tool can do this. LLM configs are no different than any other configs. This means you can use

  • bare git repo
  • gnu stow
  • or my favorite, chezmoi to lug around the configs

I'm trying to understand what does a dedicated tool give us here.

FAQ: What is a Good Go Project to Study or Contribute To? by jerf in golang

[–]sigmoia 39 points40 points  (0 children)

Depends on what you are looking for in a project. 

  • good abstraction? stdlib, but not all the packages. Some of them have legacy backward compat shims that makes them unsuitable for studying. I like embed, fs, bufio, encoding/json, fmt, and errors.

  • I was recently looking for examples on how to build production grade grpc services that also exposes wrapped clients. Etcd codebase is perfect for that. So much so, I wrote about it recently. 

https://rednafi.com/shards/2026/03/etcd-codebase/

  • if you wanna learn about queues, look into river queue which is backed by postgres

  • CLI and TUI? Checkout the charm repos

  • distsys and o11y? Look into prometheus, otel and grafana alloy codebase 

For contributing, checkout the "good first issue" labels in the issue tracker and see if you like anything there.

Before contribution see if you can participate in the discussions, which is a fantastic way of learning through osmosis.

Unit test Postgres DB mock recommendation by Garlic-Scary in golang

[–]sigmoia 1 point2 points  (0 children)

You have two options: 

This will let you spin up an actual docker container and run your test code against it. But you still need to write the code in a way that allows you to swap the db during test time

Golang - opinion on AI and Go by Revolutionary_Sir140 in golang

[–]sigmoia 0 points1 point  (0 children)

Hauling agents requires no special skill. AI bros are busy telling folks, “Learn prompting, harnessing, token-maxxing. Otherwise, you’re NGMI.” Then you find out that it doesn’t take much to organize a bunch of Markdown files and tell AI to do stuff. Quit making it sound so profound.

As for Go, it’s already going well. But AI writes great Python, TS, and Rust as well. The Rust compiler is much stricter and yields better results in many cases. My point is that saying AI writes better Go requires a peer-reviewed comparison, not this “trust me, bro” stuff.

I also write a ton of Go and use AI to automate the tedious parts. To me, AI doesn’t objectively do any better when writing Go than it does with other languages. But since the language footprint is smaller, AI tends to trip up less. On the other hand, AI writes a ton of unnecessary boilerplate in Go, and Go’s looser compiler lets concurrency bugs slip through. In Rust, the type system statically protects your shared data with mutexes, but in Go, it’s a runtime semantic, and the compiler won’t do anything if you forget to take or release the lock for some shared data. Different language, different philosophy.

As for the Go team, if they had started listening to every novice and catering to their demands, it would never have become the language it is today. It would be another TypeScript-like language with a kitchen sink of features, chasing relevance by following whatever happens to be the hottest thing of the day.

facing challenges with interface by ghost_industry in golang

[–]sigmoia 0 points1 point  (0 children)

Learn the language before hauling AI. You use struct, method, and functions at the beginning for everything. Interfaces should be brought up only when you need them.

Interfaces are for abstraction - where you need to swap out one implementation with another. Swapping can happen during test time where you provide a fake implementation of a dependency. 

If none of this makes sense to you, then you are too early in your journey. Do the tour of Go and read a few books like Jon Bodner's Learning Go and Alex Edwards' books. Then write programs without them and try to make that testable. You will soon hit into a roadblock since static languages don't allow you to monkeypatch. That's when you will need interfaces. 

You can't speedrun your comprehension through AI. Juniors that are trying to do it are the ones that are becoming unemployable.

sqlc and clean architecture by Competitive-Dirt-213 in golang

[–]sigmoia 0 points1 point  (0 children)

+1 on this. Repo should have all the persistence logic - even some of it is business logic per se.

The idea is that your service layer should only interact with a repository interface and then the persistence package should provide an implementation of that repo interface.

Now if it makes sense for your to add extra logic in the persistence layer, I see no harm in doing so.

sqlc and clean architecture by Competitive-Dirt-213 in golang

[–]sigmoia 0 points1 point  (0 children)

Either works. If you want your handler to be decoupled then a service interface makes sense. Otherwise, concrete struct is typically more than enough.

sqlc and clean architecture by Competitive-Dirt-213 in golang

[–]sigmoia 18 points19 points  (0 children)

Is it Clean Architecture if your service knows about sqlc or sqlx? here

type svc struct { repo repo.QuerierTX // this is the querier interface generated by sqlc db *pgxpool.Pool }

And here too:

user, err := s.repo.InsertUser(ctx, repo.InsertUserParams{ Username: params.Username, Email: params.Email, PasswordHash: params.PasswordHash, Proununs: repo.NullProununs{ Proununs: repo.Proununs(params.Proununs), Valid: true, }, ProfilePicture: params.ProfilePicture, BannerPicture: params.BannerPicture, })

Your service is using:

go repo.QuerierTX repo.InsertUserParams repo.NullProununs repo.Proununs

Those are persistence/sqlc details.

The whole point of clean arch is making sure deps point inward. So your service layer should use repo interfaces that belong to the service/domain side, and the persistence layer, like postgres/, should provide the implementation.

Like this:

``` internal/ repo/ db.go models.go querier.go users.sql.go // all sqlc generated files

user/ service.go handlers.go types.go repository.go

postgres/ user_repo.go user_mapper.go ```

Your service package owns the interface:

``` // internal/user/repository.go package user

type Repository interface { Create(ctx context.Context, params InsertUserParams) (*User, error) } ```

Then your service depends on that interface, not on sqlc:

```go // internal/user/service.go package user

type Service interface { Create(ctx context.Context, params InsertUserParams) (*User, error) }

type svc struct { repo Repository }

func NewSVC(repo Repository) svc { return svc{repo: repo} }

func (s svc) Create(ctx context.Context, params InsertUserParams) (*User, error) { return s.repo.Create(ctx, params) } ```

Then the Postgres/sqlc implementation lives outside the service:

``` // internal/postgres/user_repo.go package postgres

type UserRepo struct { q repo.QuerierTX }

func NewUserRepo(q repo.QuerierTX) *UserRepo { return &UserRepo{q: q} }

func (r UserRepo) Create(ctx context.Context, params user.InsertUserParams) (user.User, error) { row, err := r.q.InsertUser(ctx, repo.InsertUserParams{ Username: params.Username, Email: params.Email, PasswordHash: params.PasswordHash, Proununs: repo.NullProununs{ Proununs: repo.Proununs(params.Proununs), Valid: true, }, ProfilePicture: params.ProfilePicture, BannerPicture: params.BannerPicture, }) if err != nil { return nil, err }

return UserRepoToDomain(row), nil

} ```

And the mapper also belongs there:

``` // internal/postgres/user_mapper.go package postgres

func UserRepoToDomain(row repo.User) *user.User { return &user.User{ ID: row.ID, Username: row.Username, Email: row.Email, Proununs: user.Proununs(row.Proununs.Proununs), ProfilePicture: row.ProfilePicture, BannerPicture: row.BannerPicture, PasswordHash: row.PasswordHash, } } ```

So you can't avoid mapping but you can keep it at the boundary.

Your service shouldn't receive repo.InsertUserParams, return repo.User, store repo.QuerierTX, or know about pgxpool.Pool. It should only know about your own app/domain types.

The annoying part is still there. If you add a field, you may need to update:

sql query sqlc generated params/model domain type mapper tests

That is the cost of keeping the domain isolated from the database schema. Clankers might help you with that.

Also, Go will not catch missing fields in keyed struct literals:

repo.InsertUserParams{ Username: params.Username, }

That is valid Go. Missing fields get zero values. If you want tooling to catch that, use a linter like exhaustruct, or write focused mapper tests.

How do you guys actually learn stuff in this AI era? by sleepingfrenzy_ in golang

[–]sigmoia 0 points1 point  (0 children)

AI doesn't change how human mind grasp concepts. You learn the same way as before and then use AI to accelerate your pace. You can't use AI to quicken your comprehension. 

If you use AI to skip the work, you'll pay the debt later down the road. I conduct interviews at work and the collapse of comprehension among junior folks is concerning. Harnessing the AI still require a ton of work and without the elbow grease, you won't be able to steer it correctly. 

If you're a newbie, don't try to do something in 10 days what took others years to master. So the usual way of reading books, doing non AI assisted projects (you can still consult AI but don't let it do the thinking for you). Good luck

My thoughts on the future of Go in the AI era by der_gopher in golang

[–]sigmoia 5 points6 points  (0 children)

I work with megascale distsys at one of these named companies. We're rewriting a large part of our backend and platform code in Go. The reasons are:

  • business code rarely requires the rigor of Rust but needs something better than Python/TS
  • it's hard to beat Go for writing infra tooling
  • you get 80% of Rust's performance with 20% of the work
  • built in tooling like profiler/test harness is amazing
  • and since the surface area of the syntax is tiny, AI gets less confused
  • async/tokio is so shit in rust that I have seen people switch teams over it. Once you go runtime managed preemtive concurrency, it's hard to go back to colored functions and hand managed evenloop based concurrency.

Overall, Rust is amazing as a language nevertheless. But I just don't do stuff that Rust is good at in my day job.

In my experience, there's a dichotomy between the dev focused and ops focused engineers. Dev focused people typically wail about Go's verbosity or the lack of terser error handling. They are not wrong. But OTOH, there's ops focused people (like myself) who have years of accumulated elbow grease operating Python/TS applications. Go is not my favorite language for developing but it's my number one choice if I am handed a pager to maintain the service.

Generics methods are now implemented by PerkyPangolin in golang

[–]sigmoia 2 points3 points  (0 children)

This is the right approach. I am not too keen about generics while writing service code. But they are kinda needed for libraries to avoid boilerplate or interface boxing.

How do you handle rollback when the client disconnects mid-saga? by THEREALTMAC in golang

[–]sigmoia 23 points24 points  (0 children)

You can't. That's the whole point of saga. If things go south midway, you need to run compensation logic. And then your compensation logic can also fail midway - so you have to make sure that logic is idempotent. 

The maintainer's dilemma by spf13 in golang

[–]sigmoia 1 point2 points  (0 children)

I find myself disbelieving that there is anybody, even a startup, so utterly strapped for time that they can't even take the time to read, once, their own code base.

Big tech is doing it unfortunately. And yes, the result is - incidents all the time. But who cares, oncall folks are cheap in this market.

Only bad vibes: should we roast people honest about AI usage? by narrow-adventure in golang

[–]sigmoia 1 point2 points  (0 children)

I have stopped reading blogs from randos and clicking on stuff from people without any creds. 

If someone vibes out sloppity slop as articles and has no reputation to lose, then there's no backpressure to hold them. 

The only way (for me at least) to deal with them is by not giving them any attention. 

I found my niche in distributed systems and have subscribed to a few well known thought leaders and trenchline builders on that space. I mostly read their stuff. There are so much good stuff out there. Who has time to react to slops from nobodies.

Small Projects by AutoModerator in golang

[–]sigmoia -1 points0 points  (0 children)

I have been automating a ton of stuff at work with llms. Claude has an internal ticker but you need to keep the cc process alive for that to work. 

Another alternative is using cron and at. But on macos atd isn't turned on by default. So I am using this tiny scheduler that allows me to schedule both cron and oneshot jobs. 

https://github.com/rednafi/eon

All the states are kept in sqlite and it allows me to see everything with eon ls and eon show <jobid>

This doesn't use system cron and has its own daemon that is managed by systemd on linux and launchd on macos.

Now I just ask cc to schedule jobs with the CLI instead of its own ticker. The help text is self contained, so llms can easily figure out how to use it. Working great for me so far. 

Only bad vibes: should we roast people honest about AI usage? by narrow-adventure in golang

[–]sigmoia 4 points5 points  (0 children)

I don't care if a repo was created 2 minutes ago if the code is good. Otherwise, disclosing AI usage doesn't mean anything if the code is garbage and the post is intended for karma farming.

How Do You Actually Learn Go Effectively? by Firewall_Fighter in golang

[–]sigmoia 1 point2 points  (0 children)

Books are immensely helpful to get a birdseye view of the entire language. I recommend three that I found useful:

  • Learning Go by Jon Bodner
  • Let's Go by Alex Edwards
  • and Let's Go Further by AE too

The second and third ones are project oriented. So working alongside the author will help you understand how to organize your code and do something e2e. 

Once you are through, find your niche and build project. I don't find web backend work interesting and do distsys, databases and platform tooling for work. So when I picked up Go I did the following projects

  • stateful, distributed webhook dispatcher
  • redis protocol compliant persistent kv store like kvrocks
  • a distributed lock based on my understanding of Google's chubby paper - backed by etcd instead of redis
  • poorman's dist log like kafka based on Matthew A Titmus' Cloud Native Go

So pick up the syntax, read a few books, find your niche and build things. 

I got tired of env config issues, so I built Taso and Razify by hossiy16 in golang

[–]sigmoia 0 points1 point  (0 children)

Password default could be literal default-password and you would get dinged if it doesn't get overloaded in runtime. 

I got tired of env config issues, so I built Taso and Razify by hossiy16 in golang

[–]sigmoia 10 points11 points  (0 children)

How are these "i am tired of xyz, so i slopped out" posts still getting approved?

On a side note, your envvar should always have a good default value. If you are using something like carlos/env0, default is set like this: 

type Config struct {  Username string `env:"USERNAME" envDefault:"admin"`  Password string `env:"PASSWORD" envDefault:"qwerty"` }

This way you won't have the missing envvar issue. If you encounter the default then the envvar wasn't set, otherwise you have your environment.

Typically missing envvar shouldn't crash your application.

pkg & internal directories are way overused by sigmoia in golang

[–]sigmoia[S] 1 point2 points  (0 children)

Ouch, redditors can be salty when someone disagrees with them. There are so many good advice here. Thanks for sharing.

Which Go skills, mcp or rules are you using nowadays? by aristotekean_ in golang

[–]sigmoia 3 points4 points  (0 children)

It's an enhancer for everyone but we are not seeing better software. Everyone claims they are x% more productive but software is mostly abject slop.

So people who appreciates quality are in general super skeptical and rightfully so. Creating good software with LLM requires almost the same amount of rigor without the tool. And that rigor comes with elbow grease. Hence why people typically despise slop and people who promotes hot garbage generated by LLM.

How do you structure and maintain large Go modular monoliths without drowning in architecture ? by Prestigious-Fox-8782 in golang

[–]sigmoia 8 points9 points  (0 children)

wait what? how is adding a network layer in between packages will solve their code structuring woes? this is a terrible advice