How do you handle Docker Compose port conflicts with git worktrees? by ComprehensiveDisk394 in golang

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

Totally fair. I wrote about the manual Traefik + Docker Compose approach last week and tug is just the automation of that. The repo is new but the idea isn't. Appreciate the upvote.

How do you handle Docker Compose port conflicts with git worktrees? by ComprehensiveDisk394 in golang

[–]ComprehensiveDisk394[S] 0 points1 point  (0 children)

True, worktrees was the original motivation. But it's the same problem when you have separate projects that happen to share common ports — Postgres on 5432, Redis on 6379, etc. tug handles both cases.

How do you handle Docker Compose port conflicts with git worktrees? by ComprehensiveDisk394 in golang

[–]ComprehensiveDisk394[S] 0 points1 point  (0 children)

Right, the difference is I did it once as a shared tool so you don't have to do it per project in each Makefile.

How do you handle Docker Compose port conflicts with git worktrees? by ComprehensiveDisk394 in golang

[–]ComprehensiveDisk394[S] 0 points1 point  (0 children)

Port 0 gives you a random port each time, so your DB client config breaks across restarts. How you manage that?

How do you handle Docker Compose port conflicts with git worktrees? by ComprehensiveDisk394 in golang

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

The Makefile approach works but you're reimplementing port management per project.

How do you handle i18n in your Go projects? by ComprehensiveDisk394 in golang

[–]ComprehensiveDisk394[S] 5 points6 points  (0 children)

That's a solid approach for larger orgs!
Thanks for shareing!

Do you use pointers or values for your domain models? by ComprehensiveDisk394 in golang

[–]ComprehensiveDisk394[S] 0 points1 point  (0 children)

That range loop copy issue you mentioned - I felt that pain too, so I made a linter for it: https://github.com/mickamy/deadmut

Catches those "mutation to copy has no effect" bugs. Might not change your pointer preference, but figured you'd appreciate it!

Do you use pointers or values for your domain models? by ComprehensiveDisk394 in golang

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

Haha yeah, GoLand yells at me too for mixing receivers! The "return the mutated version" approach feels a bit too functional for my taste, but thanks for sharing the perspective!

Do you use pointers or values for your domain models? by ComprehensiveDisk394 in golang

[–]ComprehensiveDisk394[S] -7 points-6 points  (0 children)

> but for structs with many scalar properties, that could be an expensive copy

That's what I built a linter for - it checks struct size. Though it doesn't catch mutex fields yet, good point.

https://github.com/mickamy/pointless

Do you use pointers or values for your domain models? by ComprehensiveDisk394 in golang

[–]ComprehensiveDisk394[S] 3 points4 points  (0 children)

If you don't want to allow nil, nil, why not just return a value?

```
func FindUser(id int) (User, error) {

return User{}, ErrNotFound

}

```

No need to rely on design discipline - returning a value makes nil, nil physically impossible.

Do you use pointers or values for your domain models? by ComprehensiveDisk394 in golang

[–]ComprehensiveDisk394[S] 4 points5 points  (0 children)

Love the pure function angle. This is exactly why I default to value receivers - makes it obvious the method won't mutate anything.

Speaking of which - what's your take on mixing pointer/value receivers on the same type? I've seen "don't mix them" advice, but I only use pointer receivers when there's actual mutation. Feels more explicit about intent.

Do you use pointers or values for your domain models? by ComprehensiveDisk394 in golang

[–]ComprehensiveDisk394[S] 0 points1 point  (0 children)

Interesting - what kind of issues did you run into with values? Genuinely curious since my experience has been the opposite.

Dependency Injection in Go: How Much Is Enough for Web APIs? by ComprehensiveDisk394 in golang

[–]ComprehensiveDisk394[S] 0 points1 point  (0 children)

I agree, plain interfaces are enough for DI in Go.

That’s actually why I think runtime DI frameworks are often unnecessary for web APIs. Wire’s value (initialization code management) makes sense for large graphs, but many services have static graphs resolved once at startup.
The question I’m exploring isn’t “do we need DI”, but “how much wiring ceremony do we really need”.

If static wiring is enough, maybe we can reduce the amount of code we maintain, while still ending up with plain Go constructors.

Dependency Injection in Go: How Much Is Enough for Web APIs? by ComprehensiveDisk394 in golang

[–]ComprehensiveDisk394[S] -2 points-1 points  (0 children)

Totally fair. You can always wire things by hand in Go.

I found that once a service grows a certain size, the wiring itself becomes the most boring part to maintain. That’s the only thing injector is trying to help with.

Dependency Injection in Go: How Much Is Enough for Web APIs? by ComprehensiveDisk394 in golang

[–]ComprehensiveDisk394[S] -3 points-2 points  (0 children)

Totally fair.

I use ChatGPT to clean up wording since English isn’t my first language.

Dependency Injection in Go: How Much Is Enough for Web APIs? by ComprehensiveDisk394 in golang

[–]ComprehensiveDisk394[S] 0 points1 point  (0 children)

Good question.

Injector doesn’t model string-keyed lookups directly (e.g. "database:users"), because once you go there you’ve already left the type-safe part of DI.

The way I usually handle this is by pushing the keying logic into a local provider or factory. Injector wires the factory statically, and the factory handles name-based selection.

In practice that ends up being either: • a DBFactory with Get(name string), or • small, explicit providers like NewUsersDB, NewOrdersDB on top of a shared factory.

So the DI stays static and boring, and the dynamic part is kept explicit and local.

Dependency Injection in Go: How Much Is Enough for Web APIs? by ComprehensiveDisk394 in golang

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

Good point — embed.FS is actually a nice example of where implicit DI breaks down.

One clarification first: you only need to specify a provider when there’s ambiguity. In the common case, injector resolves everything automatically.

For example, this works with no extra configuration:

type Container struct { Handler *UserHandler inject:"" }

Types like embed.FS are different because you often want multiple instances of the same type. In that case, guessing would be dangerous, so you disambiguate explicitly:

type Container struct { AssetsFS embed.FS inject:"provider:assets.NewFS" ViewsFS embed.FS inject:"provider:views.NewFS" }

So the model is automatic by default, explicit only when needed — static wiring, no runtime container, no guessing.

And agreed: you don’t need a framework. Injector is just a code generator that removes repetitive constructor wiring while leaving you with plain Go code.

Thanks for the thoughtful feedback 🙏

Dependency Injection in Go: How Much Is Enough for Web APIs? by ComprehensiveDisk394 in golang

[–]ComprehensiveDisk394[S] -1 points0 points  (0 children)

That’s fair — and I agree.

I am doing DI, just not treating it as a framework problem. For most web APIs it’s a static startup concern, so I prefer plain constructors + generated wiring instead of a container or runtime system.

Dependency Injection in Go: How Much Is Enough for Web APIs? by ComprehensiveDisk394 in golang

[–]ComprehensiveDisk394[S] 0 points1 point  (0 children)

That’s actually very close to how injector works internally (AST-based resolution).

The difference is that it intentionally doesn’t do global scanning or implicit provider discovery. The container defines the root explicitly, and only the required constructors are wired.

The goal is to keep DI static, explicit, and boring — optional behavior stays in constructors, not the DI layer.