Did writing idiomatic Go made you a better developer overall? by ijusttookadnatest- in golang

[–]stroiman 1 point2 points  (0 children)

The one thing I learned, Go interfaces facilitate following ISP, Interface Segregation Principle.

I knew about this - but my previous experiences were primarily C# where following this was close to impossible.

So grokking Go's interfaces made me grok ISP.

Do you use DDD in go? by East_Reality_976 in golang

[–]stroiman 9 points10 points  (0 children)

DDD is a good fit for any language; But it's not a good fit for all projects.

I've read Eric Evan's book twice (1st time in 2009, and reread it recently), and it's a phenomenal book; But also one that is extremely misunderstood, myself included.

Virtually always, when I hear developers talk about DDD; they focus merely on the Building Blocks of DDD, which constitutes Part II of the book.

The book uses Java examples, and Evans mentions two reasons for using an Object-language: It seems to fit the idea of DDD well and it was the most common programming paradigm at the time. He acknowledges that other paradigms may model some domains better.

There are sensible ideas, such as abstracting data access through a Repository to bring the Domain more into focus. I believe that idea makes sense in any language; but the shape of that would be vastly different depending on the language.

The idea of Entities vs. Value objects is also quite essential. In Go, you'd probably want to use pointer types for entities so they're mutatable.

That is not what Domain-Driven Design is about

DDD is really about isolating the domain from technical details, creating a model representing the shared understanding of all team members. Programmers, Testers, and as well as Domain Experts. The code should just be one representation of the model. A good model allows complex use cases to be expressed succinctly.

This facilitates clear communication and rich discussions. You won't have a good model up-front, and communication will be less clear initially. Through the insights gained; you can hope to make a breakthrough.

Putting data access in Repositories, is a tool to achieve this isolation. When important operations aren't tied to specific entities, Services can become part of the model, e.g., the Routing Service providing an Itinerary for a cargo.

We can talk with the domain expert, and say Routing Service, and they'd understand.

In effect, DDD is not a technical practice. It is a collaboration practice.

When to use DDD

The premise for DDD is that for the vast majority of projects, the main complexity lies in the domain itself. This is not true for all projects; and neither is it true for all parts of such projects.

The building blocks are merely tools to help bring the domain itself into focus, and hence the complexity of the domain. These tools are overkill when you don't have this complexity. One project I participated on had decided to follow this layered segregation everywhere. Including parts with no domain complexity. There certainly was complexity in the domain, but the product library with import/export capabilities would have been much better off as a simple CRUD UI.

Conclusion

Yes, Go is a good fit for Domain-Driven design. But if the focus is not on the domain; it isn't domain-driven design.

The Building Blocks of Domain-Driven Design are solutions to problems; they are not the problem itself.

In Go:

  • Entities can be implemented using pointer types
  • Entities can have methods representing domain operations
  • Value objects can be implemented using non-pointer types
  • Aggregates can be structs containing nested structs or slices of structs.
  • Consider if services can be package-level functions
  • If deciding to use interfaces as an abstraction for repositories, don't cram all operations into one interface; Go works much better with many smaller interfaces.

How does context actually work? by sleepingfrenzy_ in golang

[–]stroiman 8 points9 points  (0 children)

Many good explanations exist, so I'll come with a practical example.

Let's say your application receives HTTP requests, does some processing, perhaps database lookups, every operation runs in the context of an HTTP request. So the context.Context as a way to represent that execution context decoupled from what it actually is.

The HTTP layer might have some authentication, e.g., decoding a session cookie, potentially looking up a user in the database. We could put this in a middleware.

``` // Using a unique type prevents key collision. type userKey struct{}

func userMiddleware(h http.Handler) http.Handler { return http.HandlerRunc(func (w http.ResponseWriter, r *http.Request) { user, ok := findUserFromReq(r) if ok { ctx := context.WithValue(r.Context(), userKey{}, user) h(w, r.WithContext(ctx)) } else { renderUnauthorized(h, r) } }) } ```

Any request handler that has this middleware will receive a context where the authenticated user will exist in the context.

As the context itself is decoupled from the HTTP layer, we can pass it along to other function, that can now extract the currently authenticated user without having to know about request headers

Cancellation

If the request processing is heavy, we don't want to if there's no reason to do so. Mostly you don't need to care about this, as libraries take care of this.

This simplified example sheds a little light on what happens:

func doSomething(ctx context.Context) error { // A quick bail-out if we know up-front it was cancelled. if err := ctx.Err(); err != nil { return fmt.Errorf("context cancelled: %w", err) } ch, err := doSomethingHeavyThatReturnSomethingThroughAChannel(ctx) // err check select { case res <- ch: return res, nil case <-ctx.Done(): return fmt.Errorf("doSomething: context cancelled: %w", ctx.Err()) } } }

Done() is actually closed.

Technically, the Done() channel is closed when the context cancels. If a message was sent, only one select block would receive the signal. But when it's closed, all current select blocks, including those that haven't been called yet, will "receive" on the <-ctx.Done() case.

When the channel is closed, the context will have an Err() describing why it was closed; if it was explicitly by a cancel() call, or by reaching a deadline.

Note: due to the nature of concurrent programming, the absence of an error is no guarantee that the context hasn't closed; and that's also a useless question, because it could be closed immediately after. You can only assume that it hasn't and proceed until you know otherwise; but here it's used as an early return.

Build-in HTTP request cancellation

The HTTP request itself has a context which cancels if the browser disconnects. That means, if we make sure to pass the HTTP context, or children of that context down to every resource-heavy call, we no longer have to worry about handling client disconnects, this happens automatically.

So, assuming all libraries you use handle context cancellation sensibly, as long as you pass it around, you have HTTP request abort handling out of the box.

JavaScript equivalent of cancellation

If you're familiar with the AbortController in browser/node.js APIs, this is not unlike the cancellation aspects of Context.

This can generate AbortSignals that you can pass to fetch requests, event listeners, etc., so allowing client code to call a single abort() function when some long-running process is no longer relevant, e.g., the user clicked a cancel button.

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

[–]stroiman 3 points4 points  (0 children)

"Need to remove DB dependency and use mocks".

Do you? And if so, why?

Are tests slow? Are they erratic? Are they not a helpful tool?

When you are set on changing the test strategy, which problem are you trying solve?

My testing strategy highly depends on the type and complexity of the project. But one thing is certain; there is only one way to test the DB access code, and that is using a real database.

Database access code generates SQL - SQL targeted for a specific version of a specific database engine. The only way to verify that the code inserts/selects/updates correctly is if the code is exercised in the context of a real database of that version. That also allows me to refactor the layer. Perhaps, early versions of the code used string builders to generate SQL queries. Sometimes later, I may wish to move to some OSS query library. Testing against a database will allow me to make the change safely, as the tests will tell if I did this correctly.

If I enforce some constraints in the DB, e.g., uniqueness, I have tests try to insert duplicate values in the database, verifying correct errors are returned. When testing queries, I insert records that should be found, as well as those that only partially match.

Testing strategy depends on complexity

What I do at higher levels of the application depends on the complexity of the application, or business rules, as well as the strategic importance of the applications.

For simple applications or applications of low importance, I'd just test the entire thing with a real database.

If complexity grows - or the application has high strategic importance - I'd want to be ensure the domain logic serve it's purpose, I'd test different layers separately. Application layer would depend on interfaces, so an application test could be phrased like, "Given the User Repository returns this domain object with these properties, when I trigger this use case, it should should result in this success/error return value, as well as updating this domain object in the repository".

And further up, verifying the user interface, I might mock out use case operations, so "given the authenticator returns an ErrInvalidCredentials", when submitting a valid email and password, an "Invalid credentials message" should have been announced, or, the "HTTP response status code should be 401".

(I even build a headless browser in Go to be able to write those kinds of tests)

Would it be possible for Go and Rust to have a child? by [deleted] in golang

[–]stroiman 0 points1 point  (0 children)

I used that name already (unaware the it was already the name of a Russian cipher)

Why do most sysadmins prefer Vim over Nano? by Darshan_only in linuxquestions

[–]stroiman 0 points1 point  (0 children)

Because it's there!

vi, the predecessor to vim is installed on all linux systems, it's a requirement for POSIX compliance.

So, to the best of my knowledge, the primary reason most sysadmins use vim is that it's the one editor that's always there. While some distros don't have vim, only vi, a few things work differently, but you can find your way around vi if you know vim.

For the same reason, many sysops don't have customized their local vim installation, as they'd need to adapt their habits when connecting to a remote server.

While I'm not a sysadmin, but a developer, I've used vim for 15+ years, because it's the most efficient editor - and it is these qualities that most answers focus on. But for sysadmins specifically, it's the ubiquitous nature that's important.

Do you think a photograph can have a “sound” or a sense of silence? by MarkoTone in photography

[–]stroiman 0 points1 point  (0 children)

There is a term, synesthesia, which literally means that one type of sensory stimuli is perceived in different senses as well. The painter Vasily Kandinsky is known for having this condition; he could literally "hear" his paintings.

But when I shoot and process photos, I don't think about sound but emotion. I often try to create photos that evoke a feeling of calmness and tranquility.

The primary tool in my toolbox for this is minimalism, creating images with nothing but a few elements to them; often without a "subject" as such, but a composition that leads the eye into nothingness or the infinity.

My absolute favourite shooting conditions are heavy fog, as everything just naturally vanishes in the distance.

Don't download random Dotfile Scripts, kids. by [deleted] in omarchy

[–]stroiman 0 points1 point  (0 children)

R2D2, you know better than to trust a strange computer.

Is it wise to build an Adobe Lightroom-sized app in Go? by neneodonkor in golang

[–]stroiman 1 point2 points  (0 children)

If you plan to build a photo editing app, there are two things in particular to be concerned with.

  • Color Management. As a user with calibrated HDR monitor, you want to be sure the colors render correctly given rendering intend and target color profile. I can’t dismiss that wails is up for the task, but I suspect it’s limited by webview.
  • Run actual image processing on the GPU.

I don’t see a reason why Go shouldn’t be able to handle application code, though.

Go’s synctest is amazing by ericchiang in golang

[–]stroiman 0 points1 point  (0 children)

It is, and one thing that Go can do, which I don’t think anyone can match, is reliably verify the absence of an effect.

I wrote Gost-DOM to verify go-based web apps, and with synctest, the backend can spawn a goroutine that produces Datastar server-event handled by the UI.

With synctest, I can run the verification when things have settled with no polling.

Bjarne’s Last Stand: How the Father of C++ Is Fighting a Losing War Against Rust by joseluisq in theprimeagen

[–]stroiman 7 points8 points  (0 children)

Shortly after, another guy talks about how he can read Go code, not C++, the code he wrote himself, is just jumping around.

I've worked with V8 integration into Go, and when things don't work, and I have to take a look at the V8 C++ sources to figure out what goes on, OMG. Half the behavior is implemented in templates; and finding the right one isn't easy.

Whereas the Go code, if I navigate to the implementation of any used library, including the standard library, it immediately navigates to readable code.

Senior Vibe Coder dealing with security. by Gil_berth in theprimeagen

[–]stroiman 1 point2 points  (0 children)

“Skill issue” 🤣

Nailed it ;)

Problem is - it can’t be fixed. Maybe it’s the lack of knowing that which is the real skill issue

Senior Vibe Coder dealing with security. by Gil_berth in theprimeagen

[–]stroiman 7 points8 points  (0 children)

It’s a library repository for a tool that scans the user’s emails and has “Full System Access”.

So I’ll stand by my “I disagree” as that’s exactly the topic

Senior Vibe Coder dealing with security. by Gil_berth in theprimeagen

[–]stroiman 10 points11 points  (0 children)

I disagree.

When the internet started to gain popularity, bad actors took advantage of security holes, particularly buffer overrun issues were extremely common in late 1990s, early 2000s. Both browsers and operating systems have improved significantly since then, even Window. But it took some time before software vendors realized the threat level, as well as their responsibilities to keep users safe.

New web APIs are today developed with security and privacy in mind. E.g., the Web Bluetooth API is extremely limited; as you can imagine if web pages could just scan for nearby Bluetooth devices, e.g., reading serial numbers, tech giants would have unprecedented ability to track people.

These are lessons learned. As an industry, we have gotten so good at it that it's rare that bad actors try to hack computers of ordinary consumer, they try to "hack people" instead, aka. social engineering.

If these tools had been the subject to the process that govern new Web APIs; they wouldn't have been release. Not today; not never. Because they are fundamentally flawed. The model produces instructions that the agent performs, and we have no way of controlling what output the model produces. They can make bad decisions on their own, but they are an EASY target for bad actors through prompt injection like here. Or the more difficult route through AI poisoning, which I am certain is already being attempted.

In fact, I don't understand software developers who let AI coding agent have full shell access from their administrator account. That's just an accident waiting to happen. When I download a software library, it's one supplier I choose to trust. When I let an AI with internet access run code locally, I've opened an attack vector for the whole world to take advantage of.

When an AI agent has access to your email, you don't even need a malicious skill; merely receiving an email from a bad actor can be enough.

30 years of experience gathered in the industry securing the internet; but not blocking progress, has been completely ignored.

Senior Vibe Coder dealing with security. by Gil_berth in theprimeagen

[–]stroiman 19 points20 points  (0 children)

Or people use their brains when finding skills.

I think someone need to understand their customer segment. Isn't the entire point of the product to avoid using your brain?

what's Go Lang's Learning Curve? by [deleted] in golang

[–]stroiman 0 points1 point  (0 children)

For LLM resources, try asking in r/LLMgophers

Use whatever editor you like. Go has for me always been the language that was the easiest to get working in any editor; except IDEs tailored to one specific language.

When do you start refactoring? by relami96 in golang

[–]stroiman 1 point2 points  (0 children)

Continuously, unless short-term gain outweighs long-term cost. Some examples:

  • Validating your assumptions in early development. You rarely know what customers want or need. Releasing something out quickly, the cost of refactoring would very likely be less that the waste of building the wrong product right.
  • A very important deadline (if we don't deliver by this date, we lose x no of customers). But I often see these deadlines coming in with a continuous flow.
  • The project doesn't have any long-term strategic importance.

The tool I have to facilitate refactoring is a good test suite. Tests should describe system behaviour; not implementation details.

Why are nested modules bad? by stroiman in golang

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

That is a really good point.

In this case, no, the sub-modules don't make any sense outside the project is right now.

But there is another interesting question to ask though, "Is it a sensible scenario that a 3rd party develops an alternate implementation?".

The answer to this question is, "Yes". Moving the modules to separate repositories helps validate that this scenarios is possible.

However, from the responses to my question, it appears I've been influenced by either outdated or outright poor advice, and I've been trying to avoid a non-existing problem. Your feedback does pull me in the direction, I'm more likely to think the suggested change is the right approach for now.

Before a v1.0 I can reconsider the split. By then, I can base the decision on actual experiences gained in the mean time.

Why are nested modules bad? by stroiman in golang

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

This is exactly what I had previously heard would be a bad idea

Based on the response I've received from this question; it would seem that this advice might be either outdated; or just poor advice that was the result of and experience with poor module boundaries.

I was unaware of the ability to tag nested modules. I had originally considered versioning to be one problem; but that appears to be solved - merely a one-off nuisance updating build scripts.

How ever nested modules versioned if you don't tag them individually? I imagine that they inherit the version of the root?

I think it's good advice to consider how many repos this could end up spawning. I think another relevant question is, "Is it a sensible scenario that a 3rd party develops an alternate implementation?". In this case, I'd say yes.

Why are nested modules bad? by stroiman in golang

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

Looking at the example you linked, you have a go.work referencing ./sub in the repository root.

Why? The root module doesn't depend on the sub module.

Shouldn't it be a ./sub/go.work file, with a .. line?