Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Table of Contents
maxLevel3

...

  • r/golang - c# vs go
    • in my opinion the biggest problem with .net isn’t the language, but rather its association with widows. Managing large projects on windows just sucks, really. (...) The other thing about .net is the community. (...) I interview a lot of .net devs and they just seem to have a kind of Microsoft centric tunnel vision. If MS didn’t publish it, it doesn’t exist.
    • We used Go for some customized ETL automation. It reminded me of Node.js. While it did the job, I moved to a different company rather quickly because I missed C#. I missed Entity & LINQ as well as all the other great features of .Net that are built-in at your disposal. Between the two, I believe C# is best for enterprise apps as long as you know how to design the entire stack well.
    • C# seems to be just a much more fun and feature rich language to use, especially when it comes to eliminating boilerplate.

    • One main advantage with Go (and Rust), both being "new" languages, is the lack of inheritance and choosing composition over inheritance, which I think is one of the main things that messes up codes in Java and C#.
    • We went with golang and language wise, it felt like it was a downgrade coming from c#. Although i do like the fast compile times, channels and goroutines, and small binary output.
    • C# has more features for sure. It’s also has a huge ecosystem. (...) Many of C# APIs just work better. For example, the File API. There’s nothing in golang that compares to LINQ (...) I also like how you can just inject services in C# versus golang. It has a much better dependency injection system and it’s not even close. (...) C# is much, much more mature than golang. (...) One thing I really like about golang is the support for grpc and protobuff. (...) It’s easier to understand go code because it’s so simple and straightforward. I also prefer error over exceptions being thrown. Not having try/catch and always checking for errors also helps you not swallow exceptions. Because goroutines only use 2kb, you require less resources to run large systems where as in c# it’s almost 1mb
    • Go literally has the best implementation of concurrency I've ever seen, that's like 50% of the reason I use Go. Go is kind of known as the fancy new concurrency language. (...) Basically Go achieves the results of high-level C++ code while being easy to use all the way from creating a project to deployment. And that's a hell of an achievement.
    • If you’re working high level services and business logic - use C#. Only consider golang if the task at hand also makes you consider c/c++. (...) their implementation of pointers is super confusing unless you've really mastered pointers in C/C++ (...) In short Golang is great at what it was created for: infrastructure code. I'm not sure it's so good at the other things, and it has way to many gotchas and hidden pitfalls to be "kind" to a new coder - take the mechanism of the pointer within a slice, for example. I'd point new coders to JavaScript, first - to get them familiar with non-strictly typed OO, first, and then move towards typescript. From there, I'd move them towards a more "concrete" backend language, like C#, or even C or C++. Heck - even Python. (...) In addition to learning Golang's syntax, you must also learn what is idiomatic, and why - which is a lot to swallow for someone who is just trying to understand what a variable is.

  • r/csharp - c# vs go
    • As much as I love Go it's strengths are kind of in niche areas. It's great to have an executable without a big runtime but the coverage of .Net Core and C# for just about everything you would ever want to develop is hard to beat. I've spent a lot of time learning Go, Elixir and Rust in the last few years and still end up using the .Net ecosystem for most solutions.
    • the only C# limitations I'm aware of are related to mobile dev.
    • C# has a heavier and more complicated runtime and has MUCH larger assemblies for self-contained deployments. Go also has a lower latency GC. This difference has let Go eat a bit into C#'s market for serverless functions, densely hosted and short duration microservices, and command line tools.
    • When I started looking, I just realized that Go doesn't have the strength in numbers that I see built around C#.
    • I'm not sure most people here understand how difficult Macs are to integrate into an existing Windows based IT infrastructure [Later person comments:I work (currently) in a bank and there are plenty of backend devs using Macbooks.
    • I have my first project in Go right now after a long time in C#, and to me it would be hard to be enthusiastic about this change. A lot of the more civilized niceties are just not there. Quick build times and small binary sizes are nice. Being close to the metal might be nice, depending on what you're doing. But most else about it feels tedious.

    • There's actually a chance that Google will eventually stop working on Go, as they are known for doing that sort of thing. (...) The Go team is something like 17 people. Let's say that pay them $1M a year, it wouldn't be a $20M project based on just salary. But .Net is $4B project and has been for 20 years now. (...) Since Go has less then 1% of 1% of the market today, there's some risk in adopting it.
    • .NET hands down. Between the (usually) well written standard library and constant stream of useful language improvements, nothing else comes close.
    • I found [Go] incredibly tedious to write
    • I had to use Go for a Grafana backend, it was an atrocious experience. It just isn't there for productivity imo.
      • Channels and goroutines are kinda nice, but don't really give you more than similar constructs in c#.

      • Defer is.... idk.. not really any better than finally. It keeps cleanup with declarations, which is nice, but out of order from an idiomatic perspective, which isn't.

      • Error handling is atrocious and really demonstrates the reality of what the "just use tuples/options" crowd want. Every fucking call is res, err = ... followed by if err != nil return nil, err kinda shit -unless you use the whole panic/recover setup which is clunky af.

      • No fucking generics. This is a huge pain in the ass for dealing with reactive extensions. Worse, the opinionated formatter will turn an inline cast of like .struct{foo,bar} into FOUR lines. [Generics were added to Go in November 2022]

    • I like [Go] for small things. Little apps and programs that patch together code that needs to be performant. I just cannot see how go can stretch to enterprise like c#.

    • [Go's] core strength make it a good choice for only fraction of use cases compared to languages like C#. (...) For example, while you technically can create a client/UI app in Go, C# would be a much better choice for basically all platforms: web, desktop, mobile.... On the other hand, anything server related, ie anything that processes a lot of different requests in parallel, Go doesn't just 'look good', it outshines because of the lightweight nature of goroutines

    • I prefer more expressive languages that allow the writer to pursue a more optimal balance of readability and conciseness than you get with Go.
    • I did C#/Windows for about 15 years as my primary language. Around 7 years ago I moved to Go/Linux. I still do some C# as my company has legacy applications in it. Personally, I love Go/Linux and I find it very frustrating having to pick up Windows/C# stuff now. Everything is just quicker, leaner, more explicit and ‘mechanical’. C#/Windows feels bloated, over abstracted, full of fluff and indirection. A couple of people have commented that you can do more in C# but I’m not sure what they’re referring to specifically. (...) C#/Windows is all IOC, DI, abstract factory patterns and blah blah blah.

    • Although in performance tests that I have seen, C# REST API performance in terms of request/sec can be nearly identical, the resource consumption is far greater than Go.
    • I did not like working with Go at all. There’s tons of help online for things on C# but I struggled to get help with Go online.
    • I basically worked for about 10 years in the .NET/C# world starting from my first internship out of college and switched jobs about 6 months ago. I am a C# fanboy and for the most part have really enjoyed learning and working in Go.

      • Things I like:

        • Very explicit "style guide" -- there's a "Go"-way of doing just about everything. Which makes writing quality code that's easy to maintain much easier than if you were making the opposite switch (Go →C#).

        • Way less verbose than c# (this can backfire at times, but in general it's nice)

        • Fast and lean - hard to overemphasize this: everything from builds to running tests is just very very streamlined

        • Implicit interfaces - love this feature

        • Testing in Go is much more integrated and feels simpler to do than in C#

        • Static typing and autocompletion just like we're used to and love in the C# world

      • Things I don't like:

        • Package management is not as easy as in C#, sometimes you run into weird dependency chain issues

        • LINQ/Lambdas -- in my current job I find myself working with collections and databases way less than my previous jobs, so I haven't really had to look into this, but creating for loops to iterate over everything does feel a bit weird sometimes. Although, I will say that this has the side effect of making code more readable. We've all seen some lambda/linq-statement horrors and I don't miss having to decipher those.

      • Concerns I'd Have if I was in your position:

        • UI development - I don't work with UI, but it definitely feels like Go's wheelhouse is backend development. If you guys have frontend products, you're going to have a bad time migrating that to Go, I think.

        • Like I mentioned earlier, there's a "Go"-way of doing things, this is great when you have people around you that know the language and you have their code to look at, but if you're switching as a company and everyone is a newbie, your code might be a mess.

    • I’ve professionally written go at my last job and now professionally write c# at my current job. In my extremely limited scope, go was great for small, lightweight processes which are compiled into a single executables. IMO, serverless (although we didn’t use it) is about the perfect use case for go. Anything else, it’s a total toss up that I’d lean towards c#.

...

  • 2019 - Ask any Go developer and they will tell you how painful it is to write programs that talk to SQL databases. Go feels like it isn't even half as productive compared to working with toolkits like SQLAlchemy, Diesel, Hibernate or ActiveRecord. The existing tools in the Go ecosystem force application developers to hand-write mapping functions or litter their code with unsafe empty interfaces. (src)
  • Should you use an ORM with Go?
    • https://www.reddit.com/r/golang/comments/t3bp79/a_good_orm_for_golang/
      • Yes:

        • I've worked on implementing a bigger project in go (4 devs, 2 years of work) following the "don't use an ORM in golang" advice and we severely regret it. In this project, more than 9/10 queries are simple CRUD statements that could have been completely solved by using an ORM. They took a long time to write and there have been multiple bugs in that part of the application. The rest are a mix of queries that could have been realized with a simple query builder or by writing simple SQL statements, utilizing the underlaying database's (pgsql) native features. tl;dr: if you use a good ORM, it'll work with you, not against you. it can save you time and a lot of boilerplate code.

        • ORMs are very beneficial and they take care of a lot of things you need everywhere and for free. It doesn't mean you have to use all of the ORM's features, but just using it as a mapping and validation framework is a win. They also force a way to deal with transactions, entity caching etc which is very helpful and may benefit performance greatly. (...) Almost always it's a mixed bag with any technology.
        • I don’t understand the hate for ORM. A good ORM can reverse-engineer your db into entities for your favorite coding language, giving you type safety and thus the ability to refactor without concern for all the magic strings in your SQL everywhere. How is this not a win?
      • No:

        • From my experience, ORM-generated SQL-queries much more slower than raw SQL-queries or builder-generated queries (squirrel, for example). ORMs are useful to develop a student project or if you have a task to develop a backend application in one month. Maybe it will save your time, yeah. But it will be a headache in the real production world, when you will try to optimize your query.
        • In my case - [GORM is] super memory and cpu intensive (but we handle more than 30m req/day).
        • I don't like ORMs because they introduce a lot of API overhead and often leave you high and dry or struggling some point down the line with something that would have been trivial had you not introduced an ORM. (...) my requirements for a database package are:
          • Doesn't introduce a lot of new or unnecessary types a la sqlx or most builders

          • Works close to database/sql and maintains your access to it

          • Handles the tedium of SELECT scanning

          • Handles the tedium of basic CRUD or model operations

        • I write my SQL queries by hand and let sqlc or pggen generate them.
    • TLDR: with ORMs it's still easy to make problems the compiler can't check: The Go community has produced higher-level libraries (github.com/jmoiron/sqlx) and ORMs (github.com/jinzhu/gorm) to solve these issues. However, higher-level libraries still require manual mapping via query text and struct tags that, if incorrect, will only fail at runtime. ORMs do away with much of the manual mapping but require you to write your queries now in a pseudo-sql DSL that basically reinvents SQL in a set of Go function calls. With either approach, it is still trivial to make errors that the compiler can't check. As a Go programmer, have you ever:

      • Mixed up the order of the arguments when invoking the query so they didn't match up with the SQL text

      • Updated the name of a column in one query both not another

      • Mistyped the name of a column in a query

      • Changed the number of arguments in a query but forgot to pass the additional values

      • Changed the type of a column but forgot to change the type in your code?

      • (src)

  • Which ORM / SQL Builder should you use?
    • ORMs
      • gorm
      • hood
      • SQLBoiler
        • I'm a maintainer of SQLBoiler, and it is the best I've used (I've tried a number).
      • upper.io
        • Although I don't personally use it very often, on the rare occasions where a client wants an ORM, my default choice is upper.io. It works with a fair number of databases, and is easy to use. (src)

    • SQL Builders
      • goqu
        • goqu is an expressive SQL builder and executor
        • While goqu may support the scanning of rows into structs it is not intended to be used as an ORM; if you are looking for common ORM features like associations, or hooks I would recommend looking at some of the great ORM libraries such as gorm or hood

      • sqlc
        • Introducing sqlc - this is a good blog post
        • How to use sqlc in 3 steps
          • You write SQL queries
          • You run sqlc to generate Go code that presents type-safe interfaces to those queries
          • You write application code that calls the methods sqlc generates
      • sqlh / SQL Helper
        • Seems like an abandoned project.
        • Author explanation of why he created it
          • Doesn't introduce a lot of new or unnecessary types a la sqlx or most builders

          • Works close to database/sql and maintains your access to it

          • Handles the tedium of SELECT scanning

          • Handles the tedium of basic CRUD or model operations

          • The development of sqlh is essentially following my specific pain points when using database/sql

Testing

...

  1. Go has two basic data structures for handling lists of records: Array and Slice.
    1. an array is a fixed-length list of things.
    2. a slice is an array that can grow or shrink, like a Python list.
  2. Both arrays and slices must be defined with a single particular data type; every element must be of the same type.
  3. To declare a slice, we use the following syntax: cards := []string{}
    1. Note he doesn't write strings (plural).
    2. You can place whatever initial records you want to include in the curly braces: {"Ace of Diamonds", newCard()}
  4. To add a new value to a slice: cards = append(cards, newElement)
    1. This returns a new slice.  It doesn't modify the existing slice.
  5. To iterate over a slice use range:

    Code Block
    for i, card := range cards {
        fmt.Println(i, card)}


    1. We use the := syntax for defining the variables (which we earlier saw should only be used the first time you define a variable) because Go "throws away" the variables at the end of every loop.
  6. In the section "Reference vs Value Types" later in the course he writes that arrays are rarely used directly; slices are used "99% of the time for lists of elements".
OO Approach vs. Go Approach

...

  • https://lets-go.alexedwards.net/
  • Thoughts:
    • This book and the next one seem to be split up in such a way that you'll want to buy both of them if you want to use Go for the back-end of a web app.
  • Summary:
    • 1. Introduction - He recommends using the HTML version of the book b/c you can copy code more easily and navigate the chapters more easily. The book has you create "Snippetbox", a Pastebin type app.
      • 1.1. Prerequisites - Use the Tour of Go or Little Book of Go to get familiar with the syntax. Use go version to check your version of Go.
    • 2. Foundations
      • 2.1. Project setup and creating a module
        • The project's module path should be globally unique to avoid conflicts with other imported packages, so something like snippetbox.nathanwailes.com.
        • Create a module by running go mod init snippetbox.nathanwailes.com in the project directory and it should create a go.mod file.
        • Snippetbox: You create and run a "Hello World" main.go file.
      • 2.2. Web application basics
        • You need three things: a handler, a router ("servemux"), and a web server.
        • A servemux is just a mapping of URL patterns to handlers.
          • You use `http.NewServeMux()` to create a new servemux, and mux.HandleFunc(url, handler) to add a new mapping of URL to handler.
        • http.ListenAndServe(port, servemux) to start the web server.
        • An amazing thing about Go is that you don't need Nginx/Apache.
        • Snippetbox: You create the simplest possible web app, with just a single route that returns a text response.
        • NW: I think I'm noticing that the app seems to take longer to compile than a Python app would take to start.
      • 2.3. Routing requests
        • Snippetbox: You add two new routes that each show different text: /snippet/view and /snippet/create
        • Servemux URL patterns can be either "fixed paths" or "subtree paths". Subtree paths end with a trailing slash and act like a catch-all.
        • He shows how to do an "if" check on the "/" subtree path to have missing paths return a 404 response: check r.URL.Path and then use httpNotFound
        • Longer paths always take precedence over shorter ones; the order in which they're registered doesn't matter (unlike JavaScript and Flask IIRC).
        • The web server is smart enough to automatically redirect requests missing slashes to the version with the slash, when appropriate.
        • You can list full host names in the URL patterns as if it was an Apache config.
        • Go has a default servemux instance but it's not recommended to use it for security reasons.
        • Important: Go's servemux doesn't support routing based on the request method, doesn't support variables in URLs, doesn't support regex URL patterns.
      • 2.4. Customizing HTTP headers
        • My thoughts:
          • On my computer I can run the curl commands he has in his book, I just need to have it as curl.exe because "on Windows, curl is an alias for Invoke-WebRequest in PowerShell".
          • I'm definitely noticing that compiling is slower than starting a Flask app; I wonder if there is a way to speed it up when reloading an app I've already compiled before.
        • You can use r.Method to check the request method (GET, POST, etc.)
        • Just FYI, not the normal way of doing things:
          • You can use w.WriteHeader() to write response headers. You can just give it an HTTP status code.
          • You can use w.Header().Set(<key>, <value>) to add a new header to the "response header map".
        • The normal way to handle errors is with http.Error(w, "Some message", <some HTTP status code>)
        • The pattern of passing http.ResponseWriter to other functions is very common in Go
        • Normally you don't use strings and integers for the HTTP methods and status code, you use constants. For example http.MethodPost and http.StatusMethodNotAllowed. Full list: https://pkg.go.dev/net/http#pkg-constants
        • Go automatically sets the Content-Type header but it can't distinguish JSON from plaintext, so you'll want to set it manually if you're sending JSON: w.Header().Set("Content-Type", "application/json").
      • 2.5. URL query strings
        • You can use r.URL.Query().Get() to get the value of a query parameter.
        • The equivalent of Python's int() is strconv.Atoi()
        • He introduces the idea of interfaces by pointing out that the fmt.Printf function takes an io.Writer object and we passed it an http.ResponseWriter object instead, but it's fine because it satisfies the io.Writer interface.
      • 2.6. Project structure and organization
        • There's no single recommended project structure.
        • Don't over-complicate things. Add structure/complexity as needed.
        • For this project we'll use this structure: https://go.dev/doc/modules/layout#server-project
        • He creates cmdinternal , and ui directories.
          • cmd holds app-specific code.
            • We'll put the web app code in a web subdirectory.
            • We could add CLI executables in a cli subdirectory (NW: would this be separately-compiled or just a new Go file?).
            • He splits the existing code into a main.go file and a handlers.go file.
            • To run the code we do go run ./cmd/web
          • internal holds potentially-reusable, non-app-specific code like validation code and database models.
            • This name is significant to the Go compiler: anything in a directory named internal can only be imported by code within the parent of that directory.
            • This is like the leading underscore in Python to designate something as private / not part of the API.
          • ui holds HTML, CSS, images, etc.
      • 2.7. HTML templating and inheritance
      • 2.8. Serving static files
      • 2.9. The http.Handler interface
    • 3. Configuration and error handling
      • 3.1. Managing configuration settings
      • 3.2. Structured logging
      • 3.3. Dependency injection
      • 3.4. Centralized error handling
      • 3.5. Isolating the application routes
    • 4. Database-driven responses
      • 4.1. Setting up MySQL
      • 4.2. Installing a database driver
      • 4.3. Modules and reproducible builds
      • 4.4. Creating a database connection pool
      • 4.5. Designing a database model
      • 4.6. Executing SQL statements
      • 4.7. Single-record SQL queries
      • 4.8. Multiple-record SQL queries
      • 4.9. Transactions and other details
    • 5. Dynamic HTML templates
      • 5.1. Displaying dynamic data
      • 5.2. Template actions and functions
      • 5.3. Caching templates
      • 5.4. Catching runtime errors
      • 5.5. Common dynamic data
      • 5.6. Custom template functions
    • 6. Middleware
      • 6.1. How middleware works
      • 6.2. Setting security headers
      • 6.3. Request logging
      • 6.4. Panic recovery
      • 6.5. Composable middleware chains
    • 7. Advanced routing
      • 7.1. Choosing a router
      • 7.2. Clean URLs and method-based routing
    • 8. Processing forms
      • 8.1. Setting up an HTML form
      • 8.2. Parsing form data
      • 8.3. Validating form data
      • 8.4. Displaying errors and repopulating fields
      • 8.5. Creating validation helpers
      • 8.6. Automatic form parsing
    • 9. Stateful HTTP
      • 9.1. Choosing a session manager
      • 9.2. Setting up the session manager
      • 9.3. Working with session data
    • 10. Server and security improvements
      • 10.1. The http.Server struct
      • 10.2. The server error log
      • 10.3. Generating a self-signed TLS certificate
      • 10.4. Running a HTTPS server
      • 10.5. Configuring HTTPS settings
      • 10.6. Connection timeouts
    • 11. User authentication
      • 11.1. Routes setup
      • 11.2. Creating a users model
      • 11.3. User signup and password encryption
      • 11.4. User login
      • 11.5. User logout
      • 11.6. User authorization
      • 11.7. CSRF protection
    • 12. Using request context
      • 12.1. How request context works
      • 12.2. Request context for authentication/authorization
    • 13. File embedding
      • 13.1. Embedding static files
      • 13.2. Embedding HTML templates
    • 14. Testing
      • 14.1. Unit testing and sub-tests
      • 14.2. Testing HTTP handlers and middleware
      • 14.3. End-to-end testing
      • 14.4. Customizing how tests run
      • 14.5. Mocking dependencies
      • 14.6. Testing HTML forms
      • 14.7. Integration testing
      • 14.8. Profiling test coverage
    • 15. Conclusion
    • 16. Further reading and useful links

...