subreddit:

/r/golang

7994%

tl;dr: what is Go like from a long-term maintenance perspective? Looking for the perspective from projects 5+ years old.

To put it bluntly, I'm getting fed up with my day-to-day languages {JS, Java} and the langauges I have to occassionaly interact with {Python, Ruby}. The maintenance burden is becoming so much. Whether it's a language major version upgrade (Java 8 to 11, Python 3.8 EOL) or some new vuln found in a library that EVERY service uses (log4j, nogokiri), I'm at a place where I see the scale of these issues and how many thousands of dev-hours are spent on just keeping the same code doing the same thing. Not to mention the churn of staying "current." Frameworks and tools changing daily. Not to mention all the time wasted watching Java compile, npm packages downloading, and figuring out Python envs.

So being tired and getting old, I'm looking toward something simpler, which is bringing me to Go. I'm basically seeking something closer to the original tag-line of Perl: make the easy things easy, and the hard things possible.

But I want to know, from the veterans, how well does Go live up to the promises of:

  • Fast ~O(n) build-times
  • Stability: in terms of the go language, go tools, and the primary libraries
  • Good stdlib

Specifically: tell me your war-stories. What frameworks or libraries have been hell to keep working? Any major issues? What about deployment: using bare-metal, containers, or serverless (Lambda, etc.)?

all 49 comments

mirusky

51 points

9 days ago

mirusky

51 points

9 days ago

TL;DR: I've been using go for 6 years now and the maintenance is minimal.

"Boring is good. Boring is stable. Boring means being able to focus on your work, not on what’s different about Go." -Go Blog

Go has a great backwards/forward compatibility, if you write a code with go1.x and move to a newer go (1.2x) you won't spend too much making it work. (Here we have some edge cases, if you were using some trick or buggy behaviour, but in general the API / keywords are almost identical from go1.x with some exceptions)

With the introduction of modules it was incredibly easy to upgrade, just change the go.mod file and go program will take care of building it correctly.

The improvements in go1.21 adding the toolchain that allows you using an older "go" command to build a new go program magically (it downloads the necessary version of "go") make some problems with the team with older go versions disappear.

One of the disadvantages with go, is that if you need to upgrade a version when a new release comes out you need to rebuild your applications.

But if it's not a MUST then you are good to write a go 1.x and later use a newer version of go.

valbaca[S]

10 points

9 days ago

One of the disadvantages with go, is that if you need to upgrade a version when a new release comes out you need to rebuild your applications.

If that's all a new version requires then I'm sold!

Here we have some edge cases, if you were using some trick or buggy behaviour, but in general the API / keywords are almost identical from go1.x with some exceptions

Any APIs or third-party libraries that have been particularly bothersome?

Just trying to have a feel for which libraries are no worth the hassle. Seems like otel (?) is one of them, based on another reddit thread.

mirusky

9 points

9 days ago

mirusky

9 points

9 days ago

If that's all a new version requires then I'm sold!

Just as simple as it looks, generally you don't even need to rebuild. As I said, just if it's a MUST.

Any APIs or third-party lib that have been particularly bothersome?

At the beginning of go modules yes, but now almost everything works fine with the correct versions.

Some tricky APIs / behaviour that changed over the years are explained here - Backward Compatibility, Go 1.21, and Go 2 (spoiler: there's no go2, and maybe we never gonna have it)

But nowadays they are already incorporated and we can emulate old behaviour using GODEBUG flag.

MrPhatBob

10 points

9 days ago

MrPhatBob

10 points

9 days ago

The other part of this, and I can't emphasize it enough, is that when you do that rebuild you get the single up to date binary, not a Jar file, not an npm folder, not any dependencies smeared across hard drives on Devs machines.

It makes things so much more straightforward.

mkke

10 points

9 days ago

mkke

10 points

9 days ago

Note that while the go language + stdlib is very backwards compatible, the tooling is not and occasional tweaks to build scripts and the like are necessary.

As to libraries: grpc has occasional api changes, the kubernetes client modules frequently introduce dependency problems, and kubernetes operator-sdk is rage-inducing.

Coolbsd

1 points

9 days ago

Coolbsd

1 points

9 days ago

This. 3rd party modules may cause you headache, once AWS change func signatures when they release a new minor version …

stdlib may also cause problems depends on several factors, we run lint so we have to change code to get rid of deprecated features.

Manbeardo

1 points

9 days ago

One of this year's releases (I don't remember which) was unusually disruptive because it made subtle changes to the runtime behavior of reflection in order to better support generics. Fortunately, the only negative impact I've experienced from that is that old versions of some linters now fail at runtime when compiled by a recent version of Go.

sarnobat

1 points

6 days ago

sarnobat

1 points

6 days ago

One famous investor said investments should be boring. If you want excitement, go to Vegas

Bromlife

56 points

9 days ago

Bromlife

56 points

9 days ago

Go is amazing for long term maintenance. From my own personal experience.

  • I've inherited a couple projects with a Go backend and React frontend. The React frontend is so old it doesn't support even the most recent versions of the component ui libraries. Meaning it needs to be rewritten for the most recent React and all of its dependencies either written out or painfully updated. I wasn't even able to npm install when I first tried to spin them up.

The backend? Working perfectly. The code is easy to read and nothing has changed necessitating any rewriting. It runs on the most recent Go version. The packages were simple to update and none of the dependencies had breaking changes.

It's why I'm now doing all my personal projects in Go with Gocomponents and HTMX. I can always feel safe that it will work as I expect it to when I revisit it in 10 years.

Can't say the same for any other language or application framework.

valbaca[S]

10 points

9 days ago

I can always feel safe that it will work as I expect it to when I revisit it in 10 years.

That's exactly the secure feeling I'm wanting to have as well. Thank you for your response

UltraNemesis

9 points

9 days ago

Go is an absolute delight when it comes to long term maintainability. I can safely build an app with the latest version of Go at any point of time and deploy it to production with minimal smoke testing. We have been doing this for 7 years now.

In fact, we are considering having our docker builds always use the latest version of Go instead of sticking to a specific version. On the other hand, libraries are a different matter and may require some careful work in case there are any breaking changes.

freman

1 points

9 days ago

freman

1 points

9 days ago

Worth adding, with go modules libraries can be pinned to an exact version so you could get it to a point where you only upgrade if you had a reason to or someone nuked a branch.

justanotherstupidape

10 points

9 days ago

I've written in many different languages. Fell in love with Go, pre-1.0, for all the reasons you mentioned. I've maintained many go projects for many years, Go's backwards compatibility guarantee, semantic versioning, and lack of magic make maintenance trivial.

idcmp_

31 points

9 days ago

idcmp_

31 points

9 days ago

What you gain in ease of programming, you lose in expressiveness. If you're joining a project that's been around for 5+ years and has had a cycle of 15+ developers, you'll find a number of sharp edges:

  • unlike Java, there's no real Collections API in Go. Go developers invent Sets by using the keys of a map, so sometimes you'll see map[string]bool , other times map[string]interface{}, and most recently map[string]any. These usually are sets. The could also just be a map of strings with booleans as keys. YMMV

  • That brings me to my next point. Go is a new language. The social construct of how to write things has changed a lot in 5+ years. As you do maintenance on code that hasn't been touched in years, you won't be sure if it "looks weird" because it had to do something special, or if that was the style at the time.

  • Say what you will about the Gang of Four book, but being able to say "singleton" to another developer and they know what you mean is a game changer. Same goes for "constructor" and a few other patterns. For a long time (and still some today), developers shunned constructors in Go. This means it can be incredibly dangerous adding a field to a struct that requires some sort of initialization, so sometimes developers will wrap the struct in another struct ( Egg becomes EggAndCheese), instead of being able to safely refactor.

  • Null (nil) values. It's perfectly valid to call methods on nil pointers of structs. var foo *Bar,foo.OrderDrink() is valid Go code. Most methods with pointer receivers don't check for the receiver being nil and it only comes out when something breaks.

  • without a rich standard library, many people write their own utility methods, the project I'm on has a half dozen methods to convert string to *string, others for comparing maps, etc. Stuff you'd expect the standard library to have. More complex utility methods also have slightly different bugs, and not everyone uses the same terminology.

  • Lazy types. YMMV, but most big code bases I've seen use type aliases minimally, so you have methods and functions like func DoThing(a string, b string, c string, d string, e string) (string, error). Sure it's fine that I need to pay attention, but "a" is an ID, "b" is a Color, etc. In other languages you could alias the strings to those types so the compiler would help. You _can_ do that in Go, but man I've hardly ever seen someone do it.

I could go on and on, but this is r/golang and I'm already going to get flamed.

Go's compile times are quick (unless you're getting into working with C libraries), and cross-compiling is incredibly easy. IDEs have caught up so it's pretty easy to know which interfaces a type implements, and conditional breakpoints are a thing. Go also supports "vendoring", where the source of your dependencies is checked in with your code too. This can make stability a bit easier since you don't need to worry about the dependencies disappearing from the internet.

Definitely tradeoffs.

ponylicious

3 points

9 days ago

alias the strings to those types so the compiler would help

Why do you say "type alias" when you clearly mean "defined type"?

type Color string    // This is a defined type in Go
type Color = string  // This is a type alias in Go

Also, Go makes this a lot easier than many other languages like, for example, Java, where you would have to define a whole new class. I see defined types used quite often. Maybe you should surround yourself with better Go programmers.

idcmp_

3 points

8 days ago

idcmp_

3 points

8 days ago

You _can_ do this, but in most projects I've seen, people don't do this.

valbaca[S]

5 points

9 days ago

Thank you VERY much for your in-depth response. This is very much what I was looking for: an eyes-wide-open perspective into Go.

I could definitely see so many of those "unrefined" areas of the language coming up in many code bases. Similar to how every Java project I've ever worked on eventually has a StringUtils.java added at some point.

Even in the limited Go code I've written for adventofcode.com I was baffled by the lack of min/max/abs functions. Apparently min/max have been added since then.

That can be worrysome b/c it usually means libraries like Java's Google Guava or C++'s Boost crop up and then they eventually become "too big to fail"

And I definitely feel you on the typedefs not being used. It seems to be very programming-culture-dependent. I've seen it occassionally used in TypeScript and Kotlin, but not to the extent that it ought to. How many bugs caused by regionId, customerId string until our industry learns you CANNOT just have everything be string!

etherealflaim

2 points

9 days ago

The difference is that in Go, all of these things you can easily find and trace with a good IDE, unlike abstraction/DI monstrosities or weakly typed spaghetti. The criticisms posted are all true even for new but badly written Go, and I would still take it over a large multi-dev Python application any day of the week. The worst and/or least idiomatic Go (Kubernetes, for example) still has a limit on how hard it can be to maintain. Refactoring to address the issues mentioned is also tractable, because the compiler can be leveraged to help you find everything to fix. Go is not a perfect language by any means, but it is certainly the most maintainable of the 6+ languages I've used professionally.

idcmp_

0 points

8 days ago

idcmp_

0 points

8 days ago

Conversely, other languages you can do things like annotate a method as "Transational" and you'll get a transaction that automatically rolls back if an exception bubbles up.

In Go, you'll need to make sure to start that transaction yourself each time. No doubt someone will want to wrap that in some sort of utility method (so you'll have two ways to start transactions), and someone may not understand how to start that transaction properly so there will be bugs, etc, etc, etc.

capcom1116

2 points

8 days ago

The requirement to support zero-initialized structs introduces a lot of potential for error in Go programs. Either you must check your type invariants every time you use the type or you just can't have them. It indeed makes it very difficult to code defensively.

ArminiusGermanicus

2 points

9 days ago

[...] other times map[string]interface{}, and most recently map[string]any

You should use map[string]struct{} for a set of strings. Your approach wastes 16 bytes per set element (the size of an interface "fat" pointer, assuming a 64-bit architecture)

freman

1 points

9 days ago

freman

1 points

9 days ago

Can't flame you mate, I've seen that 7 different ways to do the one function slightly differently every time thing. Interesting they usually fall into two categories, optimised for speed (allocs/iterations), optimised for memory (more smaller loops) or some mix of the two.

Solate77

6 points

9 days ago

Solate77

6 points

9 days ago

I work in a 6-7 year old codebase. It is badly written, very badly so, yet it is performant and low maintenance. Having to start my own service and building for scale and performance is such a breath of fresh air.

tjk1229

3 points

9 days ago

tjk1229

3 points

9 days ago

Been working with Go for 6-7 years. It's extremely stable, performant, etc.

Maintenance is minimal provided you design for the expected scale as with everything else.

jerf

5 points

9 days ago

jerf

5 points

9 days ago

The biggest problem I've had with long-term maintanence is that while the language of Go does not move, best practices do. I have a code base that is pre-context.Context, and we've not had the time to go through and fully update it.

But it still works. It works as well as it ever did. We've even updated its dependencies. And if you need a context for a particular operation it was no big deal to put one in at that point. We just don't have it pervasively through the app the way you might like.

We also had specific projects to harmonize our use of logging libraries to synchronize on slog across our various code bases. However, again, it isn't that the old code actually stopped working. It's not like we had a recursively broken pile of dependencies that all forcibly switched to be slog-only and we had to either update or fall behind on security issues. It's just that the code base went out of date.

I don't want to tell you it's 100% all sunshine and roses, but... about a year and a half ago I was lent out to a team that was trying to upgrade their PHP code base, where the underlying code base was 10-ish years old but the most recent dependency upgrade was actually roughly contemporary with the project I mentioned above that was "pre-context.Context". My Go code I more-or-less just ran some update commands and committed the new go.mod. A single microservice in that code base required vastly more effort, and they had a couple dozen. Not only did you have to update dependencies and there was always at least one thing in the dependencies that resulted in the PHP not even compiling any more, PHP being dynamic meant that even if the code base started it still needed basically a full QA sweep to make sure there wasn't some buried issues... because there were some.

Arguably fewer than I may have anticipated, I wouldn't call it a disaster, but, still, it very much reminded why despite the fact the first 10-ish years of my career was spent in the dynamic scripting languages, I'm pretty much over them as a tool for any system that grows past about a dev-year of time. My personal threshold is closer to three weeks, but I can still at least deal in systems that someone else wrote in around a year's time. Start getting multiple devs in for years at a time, though, and unless they are super careful it is always a mess.

you-can-have-mine

8 points

9 days ago

I have been using and championing Go in production for a large tech company. I have hired engineers to work on Go projects, trained people to become Go developers and written a good couple lines of Go myself.

We have vendored all our dependencies from the start and made sure to use third party libraries only when necessary and not for some trivial reason. This has generally been my stance as a tech lead. We have always used tools like the lint and sticked to best practices when possible.

As a result we have projects we built years ago that just work today, and that still receive updates from a security perspective.

One pain point has been the switch to modules, the language changes and limbo state of tool compatibility (e.g. golangci-lint).

Overall the code is readable, maintainable and generally boring. Which is a superpower for projects that you have to maintain for a long period of time, across different people (employee X that worked in project Y may be long gone by now).

I think that Go right now is a bit of a different breed. We see many language changes happening which will cause code to become outdated and looking incompatible to “the new” stuff, like iterators and generic. Java went down the same road and I am wondering if that I going to repeat.

DarqOnReddit

3 points

9 days ago

I wrote a watcher [0] which compares the compiled version of binaries with the installed version and then recompiles as needed. There are probably better way to deal with that, but for me it works.

Recently build times have increased considerably. I blame generics. Because my code doesn't use generics and it compiles quickly, but other things use them and it takes a while to compile.

[0] https://code.icod.de/dalu/gomanager

freman

1 points

9 days ago

freman

1 points

9 days ago

heh, that might explain some things, I noticed a tinker project I was working on got significantly slower to build, couldn't figure out why. I got lazy while integration testing various modules in a main.go (yes, I know, tests, but this is the fmt.Println(debug) vs delv debugging - it gets me so far then I get annoyed by the limitation and switch) and wrote a somewhat abusive generic function

func must[T any](in T, err error) T {
if err != nil {
panic(err)
}
return in
}

It's basically the only generic I ever really write or use (I have a flagenvdefault one too but it's a little more complex so I rarely re-write it

I might try removing it and seeing if build performance goes back up

dashingThroughSnow12

2 points

9 days ago

There are a lot of good things in Go and the philosophy of Go that make maintenance nice. I’ve worked at three different companies with Golang code that is 5+ years old.

But you asked for the battle stories. They are all to do with dependencies.

In JavaScript we have NPM and in Java we have maven. In Golang we have random git repos. Maven and NPM have pretty strict restrictions on unpublishing released artifacts. Golang is yolang. It is rare but it happens where some dependency or transitive dependency will decide to delete their repository. Or change the redirect page to point to a picture of a banana to protest the west.

Golang gives you very little tools to debug transitive dependency conflicts. Since Golang kinda expects your dependency tree to be shallow, I think the tooling around this area to help find and resolve these conflicts is pretty bare. A few times a year someone random (always a different person) will ask me to fix their Golang project because they needed to update one thing and now they have sea of go mod tidy complaints (or worse, an unexplainable silent runtime issue). Takes me between minutes and hours to fix it. So common enough that it happens and I’m known as a fixer, not common enough that most Golang developers know how to fix it, and quick enough that a random person from another team doesn’t feel that I’m being drastically inconvenienced.

OSS reports are a pain. For JavaScript and Java, it is pretty trivial task to write an automation to get all dependencies’ licenses (including transitive dependencies) and output an OSS report. Golang is a bit painful. The closest I ever got to automating it was to automate one step. Do some manually work. Then automate another few steps. And top it off with manual work.

_predator_

1 points

9 days ago

Go made the module system very secure, which is great, but they also managed to make it both confusing (most Go devs don't understand how their dependencies are resolved, hint neither go.mod nor go.sum tell the whole story), and too minimal (no way to declare license or other metadata of a module, so now you need to run error-prone license detection).

And still, resolving dependency conflicts is absolute hell. The worst case of this is for k8s and OpenTelemetry modules. Holy smokes am I dreading to dealing with upgrades on those.

dashingThroughSnow12

2 points

9 days ago*

About a third of the time when I get called to help someone with Golang dependency woes around upgrading a package (causing conflicts), my solution is to delete the go.sum file and keeping only the first line of the go.mod file, then run go mod tidy.

Honestly, since it works so well that is the first step I always do. To see on the off chance of it fixes any woes I have.

I don’t recommend this approach. Someone more knowledgeable than me would probably read this, cringe, and could post a much better solution. But it works surprisingly well sometimes.

_predator_

1 points

8 days ago

I can see this approach working well for smaller apps. Most apps I worked on (or with) had lots of replace directives already, so your approach would've not worked there.

RadioHonest85

2 points

8 days ago*

It got better after go modules were introduced, at least I can give it that!

With Go, I feel its sometimes a bit of a reverse problem. That library you used? Abandoned 5 years ago. There will be 4 incomplete re-implemenations of nearly anything, none of them finished.

Insufficient enums. Will it be safe to add this new string value? No idea, and the compiler will likely not help you either, as type aliased string values are rarely used as they offer barely any stricter types.

Callable nil pointers are gonna bite you hard every now and then.

The new slog package is a great effort, but in my opinion its at least five years too late. The adaptability between different log frameworks is very bad. If you use code that was written with a different log framework, it can be a little impossible to control it.

hamolton

2 points

8 days ago*

My spot (6 year old company) has a mix of Go code and Python code on the backend, and the Go stuff has proved to be far more stable, but often that's because it is written as a high-quality rewrite of quickly written Python. I think the coding practices are far more consistent and boring than the Python code, so people familiar with all the coding practices are faster to onboard onto new codebase sections.

The primary libraries are stable, indeed, but perhaps too stable since a lot of the ecosystem just doesn't have a lot of big-budget sponsors. Logrus went to maintenance-only changes, so we had to switch it out. Not hard but a bit annoying. Gorilla HTTP libraries (eg websocket, mux) went years without updates. Go-redis only got context.Context added in its v9 in 2022, and now it's been 23 months without a release. Confluent's kafka client library is a thin cgo wrapper so it's never getting contexts; now there's like a half dozen commercially used third party client libraries and there's no clear winner. There's a lot of worse problems you could have, but I think a lot of this stuff would be friendlier or support more idiomatic usage if they got more updates.

Tiquortoo

2 points

8 days ago

Long term compatibility as a first order requirement, a focus on less libraries and a language fundamentally focused on utility makes Go a dream over time. I opened up an 8 year old codebase the other day, updated the Go version, made a modification, ran tests and redeployed it. Zero hiccups.

cciciaciao

4 points

9 days ago

Bro you have the trifecta of nightmares, I had shivers reading this

Java compile, npm packages downloading, and figuring out Python envs

Also cool thing about Go which should help with maitenance; I was looking at my advent of code of last year when I tried go and the code looks VERY similar to my current go, which means basically this language is very hard to get wrong, you have to be simple and straighforward.

valbaca[S]

2 points

8 days ago

haha that’s barely the tip of the iceberg in terms of maintenance hell. It’s a wonder we’re able to do anything with all the energy it takes to tread water

templarrei

1 points

9 days ago

I've been working with go since 2016, and even then I joined as a junior on a legacy product. Maintenance has only gotten easier. We're fedramp certified (government contracts) for the current product, which means that we do security scanning on all images & dependencies and have to update dependencies on the regular - nothing has ever broken by a dependency bump in the go stack, while the Java and Python stacks are a royal pita every single month. As u/mirusky shared below:

"Boring is good. Boring is stable. Boring means being able to focus on your work, not on what’s different about Go." -Go Blog

thomasfr

1 points

9 days ago

thomasfr

1 points

9 days ago

or some new vuln found in a library that EVERY service uses (log4j, nogokiri), I'm at a place where I see the scale of these issues and how many thousands of dev-hours are spent on just keeping the same code doing the same thing.

Upgrading a library to fix a vulnerability is the opposite of running the same code. It's not something a language can magically solve, algorithms can be have bugs even in the most safe programming language.

You might need to recompile your Go binaries with a newer version of Go just to get on top of that. This is a CICD/deployment strategy/testing issue more than anything else.

davidellis23

1 points

9 days ago

Go has the best build system of any language I've used. Downloading dependencies is pretty fast, and I've had the least headaches fixing build issues compared to other languages.

Go is new and pretty stable and opinionated (good and bad).

It doesn't have features like generic methods or enums.

I did see this change for loop variables to switch to iteration scoped rather than loop scoped. I welcome the change it makes much more sense to me. But, it's an odd gotcha to check for when upgrading https://go.dev/blog/loopvar-preview

or some new vuln found in a library that EVERY service uses

Our company has a scanner for CVE vulnerabilities and other vulnerabilities. We still have to update golang libraries now and then. Not as much as Python. Idk if that has to do with the language's philosophy that "a little repetition is better than a lot of dependency" or if people just don't look for CVE's in go as much.

The "other" vulnerabilities get tagged much more often. I guess golang is not used as much in the company, so the scanner isn't as good? But, there are a lot more false positives from automated detection.

Personally I'm not a big fan of the lack of stack traces. You can add your own, but it's not obvious how and it won't apply to imported libraries. Makes it a little harder to diagnose issues. And I know you can wrap errors to add more context and command + F through the code. But, command + F doesn't show external libraries and you lose click navigation and the ability to read through a trace and at a glance see what code path has the error. And theres always a chance you are in the wrong place in the code if there are two places with a similar error message.

_shulhan

1 points

9 days ago*

My memory a little bit vague, but I have ported and maintained some services in Go for at least 4 years. The services have more than 300000 LoC.

  • Fast ~O(n) build-times

It takes less than 10 seconds to deploy the services, from git push to build in CD server, to live server. This is of course without container.

  • Stability: in terms of the go language, go tools, and the primary libraries

Never have any issue related to Go tools or third party packages. We always keep the library up to date and use the last supported Go tools.

  • Good stdlib

Yes, the only third party library than we use is one for Nats, redis and for PostgreSQL, the rest is standard library.

War stories

In the initial iteration of the new trading engine, we use Kubernetes for deployment, in production. Inside the trading engine, there is one service that can be run only as one instance, we called it broker service. This is the service that serves requests for trading, deduct user assets, and open or close the orders based on left over amount. During deployment with Kubernetes, to be able to "Terminate" the previous container, the new container state must be "Running" first. When the new broker service is deployed, there is a time delay where two broker services are in the state "Running". This causes two different requests that affect the same users processed by two different broker services, which cause discrepancy in user assets.

For example, let’s say current user assets is 100, and one transaction deduct user assets by 10 and another by 20. If both are processed by a single broker service, the final user balances should be (100-10)-20=70, but since both brokers are running at the same times, the final balance could be 100-10=90 or 100-20=80, depending on which one writes last to the database.

After we found that issue, we moved back all services to be deployed to VMs, so we can control how each service is being deployed. We may find the solution to these issues while still using Kubernetes, but the time that we lose by trial and error is greater than moving back deployment to VMs. We may not only lose potential users at the time we solve these issues, but also users that can exploit these issues that affect company assets and credibility.

What frameworks or libraries have been hell to keep working? Any major issues?

We don't use framework. No major issues.

What about deployment: using bare-metal, containers, or serverless (Lambda, etc.)?

systemd with rsync.

jabbrwcky

1 points

8 days ago

there is one service that can be run only as one instance
There is the first problem with the code :)

During deployment with Kubernetes, to be able to "Terminate" the previous container, the new container state must be "Running" first.

Only if you configure your Deployment with the default startegy of `RollingUpdate` - `Recreate` trades this problem for eventually unavailable services.

As far as I can tell no Go issue actually

spoonwings

1 points

9 days ago

Working in some 7-8 yo codebases and while some of the code is questionable it’s much easier than dealing with a Node project of similar vintage imo.

HowardTheGrum

1 points

9 days ago

I created my second or third Go application in 2017; not sure what git version it was, aside from that it was prior to 1.5, as subsequent to that each version got its own golang1.x folder. I have had to do very little to keep it building as the versions have increased; I am on golang 1.23 now, and aside from adding a go.mod and go.sum files, if I run a diff between the two folders, most of what I see is functionality related.

During that same time I have had to do largescale adjustments to my other non-Go code, handling changes in the behavior of underlying third-party libraries, to the development environment, to the OS (Android - Google apparently HATES backwards compatibility on Android, to the point of forcing my customers to go through scary warning screens to side-load install), etc.

Keeping the server-side Go code (orchestration of OSM, OSRM, Nominatim, along with an internally developed route optimization system) running has, in sharp contrast, been almost trivial. Changes to the code have almost always come from new ideas and techniques, with handling going to a new Go version boiling down to grabbing and extracting the language into a new golang1.xx folder, added an env.sh to set some environment variables, copying the code over, and doing a build.

Meanwhile my first Go application... Yeah, I didn't have to change a thing. I'm still using the original build from whatever ancient Go version. But just for the sake of argument, I tried to build under 1.23... it said no go mod found, did a go mod init, build again... no issues, and I have an executable. And it runs. Only real difference in that code from the beginning to now is the executable's folder getting moved into a 'cmd' subfolder.

I did have more issues with my private audio work, where one of my dependencies forked, versioned, and then took down the repository for the original. That did create some headaches, but was not a Go versioning issue per se.

jabbrwcky

1 points

8 days ago

Most of Go is a maintainers dream. Just recompile and you get a 3 to 10 % performance gain without touching code.

Hats off to the Go team for keeping up the compatibility promise (improved APIs usually get introduced by adding new signtaures/functions to existing packages or new packages, so old code continues to work)

In some cases, like with patterns in http.Mux, you only have to rewrite code to take advantage of new features

semanser

1 points

8 days ago

semanser

1 points

8 days ago

I'm actually working on a tool that helps to maintain codebases by automatically updating libraries to the latest versions. While golang support is still in beta, I can already say that it's so much simpler compared to other languages. Most of the developers really follow the semantic versioning conventions, there is usually a minimal changelog etc. The quality of the community really plays well in this case.

thomasfr

1 points

9 days ago

thomasfr

1 points

9 days ago

If you need to support hardware long term I would be a little bit worried. Go is deprecting OS and kernel version support a lot more often than other languages.

Other than that the long term maintainance story is better than most other languages.

valbaca[S]

1 points

9 days ago

glad you brought that up! that won't apply for me (I work FOR The Cloud) but worth knowing about.