HNHacker News
TopNewBestAskShowJobs

sagichmal

2,816 karma · joined April 16, 2014

e1b1ce94085da63902d7c51d8f1e0697
submissionscomments
sagichmal··on Log by time, not by count
Yup.

Somewhat formalized: https://peter.bourgon.org/blog/2018/08/22/observability-sign...

sagichmal··on 1 Year of Coinbase as a mission focused company
This is definitely not true (shrug) Attrition has more than doubled and most engineering teams are critically understaffed.
sagichmal··on 1 Year of Coinbase as a mission focused company
Most do, though, because there does tend to be a more correct position on this class of stuff.
sagichmal··on 1 Year of Coinbase as a mission focused company
> A handful of people left

They lost close to half of their engineering team, significant percentages of their operations teams, and many senior leaders.

sagichmal··on 1 Year of Coinbase as a mission focused company
No, I don't know anyone at Basecamp.
sagichmal··on 1 Year of Coinbase as a mission focused company
I'm not sure why you think people who care about justice harass coworkers as a matter of course. It's strictly the opposite, in my (considerable) experience.
sagichmal··on 1 Year of Coinbase as a mission focused company
I’m not sure why you think people who care about justice draw that energy from the same pool that motivates their work, as if it’s a zero-sum calculus. It isn’t. People who think about things systemically tend to apply that perspective in all of their practices. And that’s the value.
sagichmal··on My Logging Best Practices (2020)
My point is that DEBUG level logging is (hopefully!) not on by default, and that this is what it makes the production log volume manageable.

My experience has been that 1 customer-facing byte tends to generate something like ~10 DEBUG-level telemetry bytes. This level of request amplification can't be feasibly sustained at nontrivial request volumes, your logging infrastructure would dwarf your production infrastructure.

sagichmal··on 1 Year of Coinbase as a mission focused company
Yes. All of the "private" correspondence he's received, which of course he cannot share, has been overwhelmingly positive. Indeed.
sagichmal··on My Logging Best Practices (2020)
Hah! OK, sure.
sagichmal··on My Logging Best Practices (2020)
I am currently at an organisation where Splunk is a bottleneck.
sagichmal··on My Logging Best Practices (2020)
Only at trivial request volume. It’s easy to overwhelm any logging system without discipline.
sagichmal··on My Logging Best Practices (2020)
Log after is SOP in any reasonably large architecture. Too much logging is a problem you encounter very quickly with nontrivial traffic.
sagichmal··on My Logging Best Practices (2020)
Logging is significantly cheaper than tracing to maintain in a usable state.
sagichmal··on My Logging Best Practices (2020)
You only emit a single log event (line) per request. Problem solved.
sagichmal··on My Logging Best Practices (2020)
Yes, it’s useful, but that occasional usefulness is substantially outweighed by it’s perpetual cost.
sagichmal··on My Logging Best Practices (2020)
Nope, this produces unmanageable log volume.
sagichmal··on Effective Go
When I read MVC I guess I take it pretty literally, in that you would have packages named model/s, view/s, and controller/s. That's what I'm speaking to. Of course, abstractly, many programs tend to be structured in some kind of layering scheme that's MVC-ish :)
sagichmal··on Things Go needs more than generics
You continue to assert that error handling adds noise to a block of code. This is an opinion, that's fine, but it's not an objective truth. In many many domains -- in fact the domains which Go targets -- error handling is equally important to business logic.
sagichmal··on Things Go needs more than generics
That is not at all what those links indicate.
sagichmal··on Effective Go
Wow! No wonder you find this tedious.

Your gateway struct hopefully looks something like

    type Fooer interface{ ... }
    type Barer interface{ ... }
    type gateway struct{ f Fooer; b Barer; ... }
    newGateway(f Fooer, b Barer, ...) (*gateway, error) { ... }
    func (g *gateway) StartTransaction(...)
That is, a gateway is something that depends on other things, modeled as interfaces, and provides capabilities as methods.

                 +--------------------+
                 | gateway            |
                 | - f Fooer          |
                 | - b Barer          |
                 | - ...              |
                 |                    |
    input -------> StartTransaction -----> output
                 | ...                |
                 +--------------------+
When you want to exercise this code, you want to construct an instance with mock/deterministic dependencies, so that you have predictable results when you apply input and receive output. That's the model: give input, assert output.

But your linked code is kind of different! Each subtest varies not the input but the behavior of the mocked dependencies. I understand the point: you want to run through all the codepaths in the gateway method. But is that worth testing? Do the tests meaningfully reduce risk? I dunno. It's not obvious to me that they do.

The use of gomock is also a big smell. Generating mocks kind of defeats the purpose of using them. I would definitely write a bespoke client:

    type mockClient struct {
     StartTransaction func(...) (xxx.Transaction, error)
     Upsert           func(...) (xxx.Result, error)
     AbortTransavtion func(...) (xxx.Xxx, error)
    }
Then each test case is simpler to express as

    for _, tc := range []struct{
     name        string
     client      *mockClient
     input       UpdateConfigRequest
     startRes    xxx.Transaction
     startErr    error
     upsertRes   xxx.Result
     upsertErr   error
     res         xxx.UpdateConfigResponse
     err         error
    } {
     {
      name:      "success",
      client:    &mockClient{StartTransaction: good, ...},
      startRes:  ...,
      upsertRes: ...,
      res:       ...,
     },
     {
      name:      "bad start",
      client:    &mockClient{StartTransaction: bad, ...},
      startErr:  ...,
     },
     {
      name:      "bad upsert",
      client:    &mockClient{StartTransaction: good, Upsert: bad, ...},
      startRes:  ...,
      upsertErr: ...,
     },
     ...
    } {
     t.Run(tc.name, func(t *testing.T) {
      g := newGateway{tc.client}
      output := g.StartTransaction(tc.input)
      // asserts
     })
    }
sagichmal··on Downsides of Offline First
CRDTs and smart contracts are almost totally unrelated to each other.
sagichmal··on Downsides of Offline First
> Is there any theoretical groundwork happening on how CRDTs can preserve domain semantics?

Effectively using CRDTs requires a fundamentally different model of state. Typical CRUD stuff simply doesn't work. If you're unwilling or unable to refactor your state layer, then CRDTs are gonna seem totally infeasible.

sagichmal··on Effective Go
I don't understand why you would reply with this comment. If the question is "how do I do X?" and X isn't something that you should be doing, why is it not constructive to point that out?
sagichmal··on Downsides of Offline First
When something is unsound it is valuable to point it out. It prevents harm.
sagichmal··on Effective Go
I'm sorry if you've had bad experiences with this approach in the past, but it emphatically does not lead to low quality and/or meaningless tests. It's the essential foundation of well-abstracted and maintainable software.
sagichmal··on Downsides of Offline First
This isn't in the relevant domain.
sagichmal··on Effective Go
"MVC" is not an idiomatic pattern in Go.
sagichmal··on Effective Go
Maintainable software projects are modeled as a dependency graph of components that encapsulate implementation details and depend on other components.

    func main
        foo, err := NewFoo()
        handle err
        bar, err := NewBar(foo)
        handle err
Given a single component, each external dependency should be injected as an interface.

    type Fooer interface{ Foo() int }
    
    func NewBar(f Fooer) (*Bar, error) { 
        ...
    }
Test components in isolation by providing mock (stub, fake, whatever, it's all meaningless) implementations of its dependencies.

    func TestBar(t *testing.T) {
        f := &mockFoo{...}
        b, err := NewBar(f)
        ...
sagichmal··on Downsides of Offline First
Because when the author writes or speaks about the project, they consistently demonstrate a fundamental lack of understanding of the domain. There is a reason that GunDB is never mentioned in literature or papers.
Page 1 of 34Next →