« BackSome Go web dev notesjvns.caSubmitted by tosh 5 hours ago
  • voigt 3 hours ago

    > In general everything about it feels like it makes projects easy to work on for 5 days, abandon for 2 years, and then get back into writing code without a lot of problems.

    To me this is one of the most underrated qualities of go code.

    Go is a language that I started learning years ago, but did't change dramatically. So my knowledge is still useful, even almost ten years later.

    • almatabata 5 minutes ago

      > Go is a language that I started learning years ago, but did't change dramatically.

      A lot of people underrate that quality I feel. C had that quality but pushed it to neurotic levels where it would change so slowly even when it needed to do it faster. Other language in contrast change too fast and try to do too many things at once.

      You do not often get credit for providing boring, stable programs that work. I hope they continue to do it this way and do not get seduced into overloading the language.

      We have a lot of fast moving languages already, so having one that moves slowly increases potential choice.

      • tmpz22 3 hours ago

        I agree but those first 5 days are going to be a mixed bag as you pick through libraries for logging, database drivers, migrations, as well as project organization, dependency injection patterns for testing, organize your testing structure, and more.

        If you have a template to derive from or sufficient Go experience you'll be fine, but selecting from a grab bag of small libraries early on in a project can be a distraction that slows down feature development significantly.

        I love Go but for rapid project development, like working on a personal project with limited time, or at a startup with ambitious goals, Go certainly has its tradeoffs.

        • jrockway 3 minutes ago

          I really think the library search is more of something you inherit from other languages, though database drivers are something you need to go looking for. The standard library has an adequate HTTP router (though I prefer grpc-gateway as it autogenerates docs, types, etc.) and logger (slog, but honestly plain log is fine).

          For your database driver, just use pgx. For migrations, tern is fine. For the tiniest bit of sugar around scanning database results into structs, use sqlx instead of database/sql.

          I wouldn't recommend using a testing framework in Go: https://go.dev/wiki/TestComments#assert-libraries

          Here's how I do dependency injection:

             func main() {
                 foo := &Foo{
                     Parameter: goesHere,
                 }
                 bar := &Bar{
                     SomethingItNeeds: canJustBeTypedIn,
                 }
                 app := &App{
                     Foo: foo,
                     Bar: bar,
                 }
                 app.ListenAndServe()
             }
          
          If you need more complexity, you can add more complexity. I like "zap" over "slog" for logging. I am interested in some of the DI frameworks (dig), but it's never been a clear win to me over a little bit of hand-rolled complexity like the above.

          A lot of people want some sort of mocking framework. I just do this:

             - func foo(x SomethingConcrete) {
             -     x.Whatever()
             - }
             + interface Whateverer { Whatever() }
             + func foo(x Whateverer) {
             +     x.Whatever()
             + } 
          
          Then in the tests:

             type testWhateverer {
                n int
             }
             var _ Whateverer = (*testWhateverer)(nil)
             func (w *testWhateverer) Whatever() { w.n++ }
             func TestFoo(t *testing.T) {
                 x := &testWhateverer{}
                 foo(x)
                 if got, want := x.n, 1; got != want {
                     t.Errorf("expected Whatever to have been called: invocation count:\n  got: %v\n want: %v", got, want)
                 }
             }
          
          It's easy. I typed it in an HN comment in like 30 seconds. Whether or not a test that counts how many times you called Whatever is up to you, but if you need it, you need it, and it's easy to do.
          • tptacek 19 minutes ago

            I think 80% of this is people coming to Go from other languages (everybody comes to Go from some other language) and trying to bring what they think was best about that language to Go. To an extent unusual in languages I've worked in, it's idiomatic in Go to get by with what's in the standard library. If you're new to Go, that's what you should do: use standard logging, just use net/http and its router, use standard Go tests (without an assertion library), &c.

            I'm not saying you need to stay there, but if your project environment feels like Rails or Flask or whatever in your first month or two, you may have done something wrong.

            • mattgreenrocks 28 minutes ago

              About 50% of the time learning a new language I find that the consensus framework/library choice is not quite to my taste. It isn’t that it’s bad, it just often feels like one thing went viral and then ends up boxed in by their success such that people double down/put up with it rather than evolve it further.

              Point being you’re probably going to spend those first five days evaluating the options. The “community” doesn’t know your taste or your needs. You have no idea what their goals are, or what the average skill level is. All of those things can make a big difference in selecting tech to build atop of.

              • joshlemer 3 hours ago

                I stumbled on this Go starter project that has enough batteries included to get you started I think. You might find it useful

                https://github.com/mikestefanello/pagoda

                • tmpz22 2 hours ago

                  Right and mikestefanello/pagoda seems like a comprehensive framework combining the labstack/echo/v4 routing framework and entgo.io/ent "ORM" - among other things like HTMX and Bulma.

                  That is a highly opinionated grouping of tools that gets you to a poor persons version of a full stack framework that many in the Go community would flat out reject.

                  • leetrout 2 hours ago

                    I really don't understand the hate for a framework in "the community". I had stayed away from Go for about 3 years and I posted on r/golang asking if anything had popped up like a django in go and got nothing but hate.

                    I chock it up to people enjoy writing the same boiler plate stuff over and over in their paid jobs where they have the time to do it.

                    To your point, I've got my own set of libraries that I think are The Best™ for all my reasons that at least keep me productive.

                    Echo for the routing... sqlc for the db interface... I actually _love_ gomega and ginkgo for BDD but it makes people actually angry so I settle for testify and for logging we have slog now but I usually grab the logging lib from charm bracelet for nice colorized, leveled logging.

                    • onislandtime an hour ago

                      Hey, I checked your Reddit thread [1] and I see a good discussion with useful suggestions. I don't see "anything but hate" at all. There are pros and cons to using frameworks. True, many people seem to prefer to use smaller libraries. I personally don't like magic because sooner or later I lose track of what's going on and need to start guessing. With direct code I can understand faster without having to read docs and layers of source code. It's all about finding the right balance of abstractions.

                      1 - https://www.reddit.com/r/golang/comments/1anmoqg/what_go_lib...

                      • pepelotas an hour ago

                        I liked gomega and ginkgo when I worked with it. But by virtue of running tests serially, the race detection becomes useless during testing, and I think it's a must have. Has it changed?

                        • cgg1 an hour ago

                          The best thing I’ve found is beego: https://github.com/beego/beego

                          Not affiliated in any way, but to me it’s better than most of the alternatives

                        • joshlemer 2 hours ago

                          And since it isn't a "framework" but rather just a wired up collection of libraries, it would be pretty simple and even a good learning process to change out the couple of libraries one doesn't like (say, in case they prefer not to use ent, and backlite).

                      • DarkCrusader2 2 hours ago

                        I had this exact same experience with Go. I picked up .Net (and Asp.Net for web stuff) on linux recently and found it to be much more easier to get started batteries included than Go. Don't need external libraries for any of the things you mentioned (ORM, logging, metrics, DI etc.).

                        Razor pages are very interesting as well. I haven't used them enough to have a solid opinion yet but I really liked them for quick server rendered pages.

                      • aczerepinski an hour ago

                        It’s why I moved my personal site from Phoenix to Go and it has proven to he a great choice. I have zero dependencies so they never go out of date.

                        • ocdtrekkie an hour ago

                          This used to be true of PHP as well, though them finally picking off the hairy bits of bad assumptions has eroded that a fair bit. For a long time you didn't usually need to concern yourself what version of PHP 4-5 or so what version you were running on.

                        • gwd 2 hours ago

                          > I learned the hard way that if I don’t do this then I’ll get SQLITE_BUSY errors from two threads trying to write to the db at the same time.

                          OK, here's a potentially controversial opinion from someone coming into the web + DB field from writing operating systems:

                          1. Database transactions are designed to fail

                          Therefore

                          2. All database transactions should done in a transaction loop

                          Basically something like this:

                          https://gitlab.com/martyros/sqlutil/-/blob/master/txutil/txu...

                          That loop function should really have a Context so it can be cancelled; that's future work. But the idea stands -- it should be considered normal for transactions to fail, so you should always have a retry loop around them.

                          • krackers 14 minutes ago

                            I always see SQLite as recommended, but every time I look into it there are some non-obvious subtleties around txn lock, retry behavior, and WAL mode. By default if you don't tweak things right getting frequent SQLITE_BUSY errors seems to occur at non-trivial QPS.

                            Is there a place that documents what the set-and-forget setting should be?

                            • arp242 3 minutes ago

                              _journal_mode=wal&_busy_timeout=200 seems to work well enough.

                            • tedunangst an hour ago

                              You'll just end up looping until your retry limit is reached. SQLite just isn't very good at upgrading read locks to write locks, so the appropriate fix really is to prevent that from happening.

                              • returningfory2 2 hours ago

                                I don't think this controversial. Retrying failed transactions is a common strategy.

                                • gwd 2 hours ago

                                  You're the first person I've heard say so. When I was learning DB stuff, there were loads of examples of things that looked at the error from a transaction. Not a single one of them then retried the transaction as a result.

                                  The OP's comment is a symptom of this -- they did some writes or some transactions and were getting failures, which means they weren't retrying their transactions. And then when they searched to solve the problem, the advice they received wasn't "oh, you should be retrying your transactions" -- rather, it was some complicated technical thing to avoid the problem by preventing concurrent writes.

                                  • bornfreddy 22 minutes ago

                                    I'm in the opposite camp. Transactions can fail, however retrying them is not the solution. It hides the symptom (which is not a problem with proper monitoring), but more importantly, it can lead to overwhelming the db server. Sometimes failure happens because of the load, in which case retrying the query is counterproductive. And even in cases where retrying would be the correct approach, it is the responsibility of the app logic, not of the db connection layer, to retry the query. Imho of course. :)

                                    • returningfory2 26 minutes ago

                                      Ah, interesting. Maybe my experience has been unusual then.

                                      I agree with you the retrying transactions is relatively simple and powerful.

                                  • rad_gruchalski an hour ago

                                    Wouldn't you need two contexts? One for retry cancellation, one for underlying resources to be passed on to?

                                  • yegle 2 hours ago

                                    It's sad https://pkg.go.dev/embed was not mentioned in a post about web development in Go :-)

                                    Having a true single binary bundling your static resources is so convenient.

                                    • dullcrisp 36 minutes ago

                                      It is mentioned now.

                                      • okibry 2 hours ago

                                        Which time golang read file, build time or run time ?

                                        • nasretdinov 2 hours ago

                                          embed package allows to embed assets directly into the binary, so the files are read once during build time and then you can access them as e.g. a byte slice, a string, or a special FS object that acts like an in-memory file system

                                          • estebarb an hour ago

                                            Build time. It literally embeeds the file in the binary.

                                            • catlifeonmars 2 hours ago

                                              Build time

                                          • rmac 18 minutes ago

                                            the whole gobin / gopath thing was annoying as a beginner: I just want to build this module and use that local module

                                            go build ./... Goes where ?

                                            • coffeeindex 4 minutes ago

                                              I believe it is recommended to not use gobin/gopath anymore.

                                              go test ./… tests all files in the project, so I assume build does something similar.

                                            • srameshc 3 hours ago

                                              Good to see author's mention about routing. I am mentally stuck with mux for a long time and didn't pay attention to the new release features. Happy that I always find things like these on HN.

                                              • JodieBenitez 2 hours ago

                                                Nice new feature, would actually make me want to use Go without Gin.

                                                • leetrout 2 hours ago

                                                  I am over Gin and have been for years yet everyone keeps using it because it has inertia. The docs are garbage.

                                                  Big fan of Echo and it has much better docs.

                                                  https://echo.labstack.com/

                                                  • JodieBenitez 36 minutes ago

                                                    Thanks for the suggestion, will give it a try. I'm more familiar with Python than Go. I know my way around the Python ecosystem and can make informed decisions about which tool to use. Not so much with Go, so I appreciate your advice.

                                                    • xerox13ster an hour ago

                                                      I had to move from Gin to echo for my personal site, the routing in Gin was refusing to serve static resources at the root path without some headache.

                                                • physicles 3 hours ago

                                                  GOMEMLIMIT has really cut down on the amount of time I’ve had to spend worrying about the GC. I’d recommend it. Plus, if you’re using kubernetes or docker, you can automatically set it to the orchestrator-managed memory limit using something like https://github.com/KimMachineGun/automemlimit — no need to add any manual config at all.

                                                  • nickzelei 41 minutes ago

                                                    Oh this is a good find. Thank you for sharing that link!

                                                  • imiric 2 hours ago

                                                    There are some good tips here.

                                                    As for sqlc, I really wanted to like it, but it had some major limitations and minor annoyances last time I tried it a few months ago. You might want to go through its list of issues[1] before adopting it.

                                                    Things like no support for dynamic queries[2], one-to-many relationships[3], embedded CTEs[4], composite types[5], etc.

                                                    It might work fine if you only have simple needs, but if you ever want to do something slightly sophisticated, you'll have to fallback to the manual approach. It's partly understandable, though. It cannot realistically support every feature of every DBMS, and it's explicitly not an ORM. But I still decided to stick to the manual approach for everything, instead of wondering whether something is or isn't supported by sqlc.

                                                    One tip/gotcha I recently ran into: if you run Go within containers, you should set GOMAXPROCS appropriately to avoid CPU throttling. Good explanation here[6], and solution here[7].

                                                    [1]: https://github.com/sqlc-dev/sqlc/issues/

                                                    [2]: https://github.com/sqlc-dev/sqlc/issues/3414

                                                    [3]: https://github.com/sqlc-dev/sqlc/issues/3394

                                                    [4]: https://github.com/sqlc-dev/sqlc/issues/3128

                                                    [5]: https://github.com/sqlc-dev/sqlc/issues/2760

                                                    [6]: https://kanishk.io/posts/cpu-throttling-in-containerized-go-...

                                                    [7]: https://github.com/uber-go/automaxprocs

                                                    • bornfreddy 12 minutes ago

                                                      I agree that sqlc has limits, but for me it is great because it takes care of 98% of the queries (made up number) and keeps them simple to write. I can still write manual queries for the rest of them so it's still a net win.

                                                    • codegeek 3 hours ago

                                                      What I love about Go is its simplicity and no framework dependency. Go is popular because it has no dominating framework. Nothing wrong with frameworks when it fits the use case but I feel that we have become over dependent on framework and Go brings that freshness about just using standard libraries to create something decent with some decent battle tested 3rd party libraries.

                                                      I personally love "library over framework" mindset and I found Go to do that best.

                                                      Also, whether you want to build a web app or cli tool, Go wins there (for me at least). And I work a lot with PHP and .NET as well and love all 3 overall.

                                                      Not to mention how easy was it for someone like me who never wrote Go before to get up and running with it quickly. Oh did I mention that I personally love the explicit error handling which gets a lot of hate (Never understood why). I can do if err != nil all day.

                                                      A big Go fan.

                                                      • jeffreyrogers 3 hours ago

                                                        I like Go for this reason as well. In Python I found the Flask framework to be suitably unobtrusive enough to be nice to use (never liked Django), but deploying python is a hassle. Go is much better in that area. The error handling never bothered me either.

                                                        I think if Go shipped better support for auth/sessions in the standard library more people would use it. Having to write that code yourself (actually not very hard, but intimidating if you've never done it before) deters people and ironically the ease of creating Go packages makes it unclear which you should use if you're not going to implement it yourself.

                                                        • leetrout 2 hours ago

                                                          I am a Django apologist because I grew up with Django. So with that being said, I'm not out to convert you but I am genuinely curious what you don't like about it. Promise I won't refute anything I just like to try to understand where it turned off folks.

                                                          I don't like flask because it seems just easy enough to be really productive in the beginning but you eventually need most of the things Django gives you. So I would rather pick up Django Rest Framework or Django Ninja than Flask or Fast API. In those cases I jump straight to Go and use it instead because the same library decisions on the Go side give me a lot more lift on the operations end (easy to deploy, predictable performance into thousands of requests per second if built correctly).

                                                          • jeffreyrogers 2 hours ago

                                                            It's opinionated in a way I dislike. I don't actually have anything against opinionated software--tailwind is very opinionated about how you should write CSS but I like it because it matches up with how my mind works. But I find Django very jarring. I can't point to a specific thing about Django, it's more that if I were to design a framework from scratch it would look nothing like Django, so I experience a lot of friction trying to shape my ideas about how to build an application into Django's way of doing it. I have the same problem with Rails as well.

                                                            I agree with you that Django provides most things that an application will eventually need, and if I were managing a team that was starting a project from scratch I think Django would be a reasonable choice, but aesthetically something about it irritates me.

                                                            • jpc0 2 hours ago

                                                              I have to learn an entite framework and if I want to stray away from what it wants the magic makes it hard.

                                                              For one, I so migrations with raw SQL onto the server, I just don't trust it any other way and I dislike ORMs, I even dislike query builders.

                                                              But for a big framework like Django you can't remove those batteries easily and you have already strayed away from the narrow paved road.

                                                              If I'm spinning up an API endpoint for my existing stack, I'm picking flask ( well no I'm picking go because WSGI is a pain in the... to deploy ) purely because I don't need auth + rate limiting + an orm and all that. I need endpoints exposed to do what I need, literally the rest is already handled by my API gateway and it will be tied into our existing management dashboard.

                                                              Django may be great to spin up a quick project but I found I needed to stary for the paved road pretty quickly so I rather picked a different tool...

                                                              This doesn't apply to everybody either, for aome Django is the correct solution.

                                                            • atomicnumber3 3 hours ago

                                                              My main gripe about go is that it's decent for the middle and late stages and really really bad to start with. You'll spend way too much time rewriting stuff you literally get for free by running "rails new" or "bundle add devise"

                                                              • vasilzhigilei 3 hours ago

                                                                I love using Go for personal projects, but I keep finding myself recreating the same redis-based session storage logic, authentication, admin vs public routes, etc. Really does burn time in the beginning, even though it's fun to write the code.

                                                                • tizzy 2 hours ago

                                                                  There is definitely space for an opinionated set of libraries and boiler plate code for golang projects like this.

                                                                  Having said that, I’d bet that the go community consensus is that you build one out yourself and reuse it. So most times I end up copy and pasting the same logic rather than recreating.

                                                                  • legorobot an hour ago

                                                                    100% this. I have a set of commonly-used code in a repository we use at work. AuthN/AuthZ, code specific to our infrastructure/architecture, common http middlewares, error types, DB wrapper, API clients, OpenAPI Server generation, etc.

                                                                    However, my personal projects have a different set of code I reuse (more CLI- and tool-heavy focus), and I'm sure other environments would have an entirely different set of reused code.

                                                                    On the opinionated library side of things, I did follow LiveBud for a while, and Go-Micro but haven't really sat well with the experiences from those, given how they lock you in to different aspects of your stack.

                                                              • kolja005 2 hours ago

                                                                I'm curious in what sense you find Python difficult to deploy? My company has tons of Python APIs internally and we never have much trouble with them. They are all pretty lightly used services so it it something about doing it on a larger scale?

                                                                • codegeek 2 hours ago

                                                                  Some great points about the downsides of Go. Btw I was a Flask junkie in early days.