top | item 39320357

(no title)

doh | 2 years ago

I think that's a valid criticism. What do you think would be a more ergonomic pattern?

discuss

order

Raynos|2 years ago

I wrote a static config class that reads configuration for the entire app / server from a JSON or YAML file ( https://github.com/uber/zanzibar/blob/master/runtime/static_... ).

Once you've loaded it and mutated it for testing purposes or for copying from ENV vars into the config, you can then freeze it before passing it down to all your app level code.

Having this wrapper object that can be frozen and has a `get()` method to read JSON like data make it effectively not mutable.

doh|2 years ago

I use similar pattern myself. Was curious if the OP is using some other, like for instance splitting the struct into two (im/mutable) and then passing them around, or what.

BTW kudos on zanzibar. Love the tech and the code).

gabesullice|2 years ago

Not the OP, but I mitigate the issue rather than use a different pattern. Like so:

type Server struct { val bool }

type Config struct { Val bool }

func NewServer(... config *Config ...) http.Handler { if config == nil { config = &Config{} } return &Server{ val: config.Val } }

It took me a long time to settle on this pattern and I admit it's tedious to copy configuration over to the server struct, but I've found that it ends up being the least verbose and maintainable long term while making sure callers can't mutate config after the fact.

I can pass nil to NewServer to say "just the usual, please", customize everything, or surgically change a single option.

It's also useful for maintaining backwards compatibility. I'm free to refactor config on my server struct and "upgrade" deprecated config arguments inside my NewServer function.

zemo|2 years ago

I just use a struct literal, and then I have the type define a `func (t *Thing) ready() error { ... }` method and call the ready method to check that its valid. I prefer this over self-referential options, the builder pattern, supplying a secondary config object as a parameter to a constructor, etc.