Moving from PHP to GO and back again
I don’t know why but my previous blog post got almost 5k views in 24 hours. I guess a lot of people are hungry for information about Go? Who knows..
Anyhow, Golang is not happening. I gave it 5 full days but Go is not the redeemer[救世主] that I got a feeling the community is trying to proclaim[宣告] it is.
Sure, I haven’t mastered the language, but I’ve learnt enough so that I can make an opinion on the technology.
First of all, Go is absolutely a systems programming language and the authors are no saying otherwise.
Go was born out of frustration with existing languages and environments for systems programming.
This might seem like an unimportant information but in this context it means that the place Go shines is low level programming(that’s also why its on par[标准杆] with C when it comes to performance).
This means that Go will be excelling when writing networking or data manipulation libraries, but that’s about it.
Trying to use it out of this scope means you will be using the wrong tool for the job.. and this is what I tried to do.
You see, there are some well known projects built with Go — like Kubernetes, Docker, basically all HashiCorp’s products, Moby, Prometheus.. and this gives you the impression that Go is a great tool for building bigger applications.
And the truth is the exact opposite. These companies/teams have picked Go most likely for the same reason Google has made it for itself. And that is — making low level language powerful enough to be as performant as C but as simple as Python, for example, and allowing large teams with different background(lower/higher level) to work together on single platform. The reasoning behind this is well known and you can simply google the reasons which I am not going to reiterate[反复地说；重申] in here.
In other words, those people have selected Go because it is a great mix of powerful and simple, allowing fast progression without performance regression[回归；退化；倒退] within teams with different programming language backgrounds. Simply, they didn’t have much choice.
For those low level tasks it’s a great tool but I will argue that it is absolutely not a great tool for anything higher-level.
Go’s simplicity works very well in your favour if you are creating a single purpose low-level library or application that is small in size and purpose.
If you look at those bigger projects, the code base is a mess, due to the way Go forces you to work by splitting code into different “namespaces”. On itself, that is not a sin. But another point I want to make is that in Go, you absolutely, under no circumstances[环境；境况；本末；(一桩)事故], should be even trying to reuse code or define an interface. Sooner or later, it will bite you in the ass.
Go is not OOP in any shape or form, no matter what “they” try to tell you. Go is imperative[重要紧急的；迫切的；急需处理的；表示权威的] language with procedural style of writing code. Yes, you have objects and you can attach methods to them, similarly to Ruby where everything is an object, but that’s basically it. Never, ever, try to repeat yourself in Go. You will crash and burn.
When it comes to Go, spaghetti code is what you want to write.
Go has composition instead of inheritance and polymorphism but you will be running in circles if you’ll try to create any re-usable code. You will fail badly.
There is a reason why Go is lacking “real” frameworks. Sure, you have Go Kit, Micro, Gin and few others but they will never be able to come even close to Symfony, Laravel, RoR, Django or Flask.
And that is because they are limited by the language itself. You cannot define interfaces on their own. It has no meaning. You have to define basic functionality that can be extended by the user. You can do this and have an interface and base struct but once the user extends the struct via composition all you binding methods will go out of the window and the user is left with having to re-implement everything anyway.
OK, so no frameworks and DRY. What does it mean? It means that when you write Go code you should not think about the future and possibilities of extending or altering the code. You should be thinking in “right now”.
You write your application directly, no traits, not plugins, no interfaces, no base classes, no helpers/tooling, nothing. Simply write the functionality for right now. Focus on your single main.go file. Coming from higher level languages, it is very tempting to start thinking in “namespaces” and sub-directories but that is a fool’s errand[差事；差使].
Go was meant to solve problems as efficiently as possible. And this collides[(车等)碰撞；(意志等)冲突] with higher level languages, like PHP, Java, C#, Python or whatever you’re using.
These languages provide you with a lot of abstractions to make writing code easier and not having to think about IOs, packets, bits and bytes, gc and whatnot.
Go is not like that and trying to make it work the way you are used to work simply won’t work. It will be painful and pointless.
I’ve chosen Go because it had good track in the mainstream in regard to micro services architecture. The high performance and small system resource requirements seem like a great match. And it is, it really is… but not really.
When the topic of microservices comes up there is no single rule that can describe what a micro service is or how it should look.
And this is the source of confusion in regard to Go. Let me explain…
As I’ve mentioned, Go is a great low level language for small efficient libraries that interact with network or IOs. But that does not mean that writing an application with thousands of line of code is also a good place to use Go.
I would argue that Go should be used only for nano services. I do not mean that as a measure of lines of code. I mean it as a representation of very simple and small unit of logic that is to be performed.
For example, Go would be perfect language to use for a micro service whose purpose is to receive events from event store in event sourced architecture and normalize them into consumable messages for the message broker to distribute to interested 3rd parties.
Such service would basically receive a binary data, unpack it, make it into JSON, do some cleaning and forward it away. Go would excel in this scenario.
It is a very simple task that does not require much logic. It could be handled by Python or PHP but Go will be in different level of performance.
Now imagine a microservice where you have to connect to a database, fetch some results, perform some business logic and send some messages out.
This does not sound much different from the previous example. But this time there is a database in play. Do we have a driver for it in Go? If yes, good for us. But then there is also the business logic. And this is what it all boils down to — can you imagine having your business logic written in plan functions? And not one or two, probably tens, most likely hundreds. Are you up for this? Will you be ok managing and maintaining this monster?
I don’t think so. Why is that? It’s because Go is out of its element. This is not place for it. Do not ask Go to do things that other languages will do better. Not faster, but better.
Human factor has to come in play in here. Writing code is not about performance — unless you are in finance.
Writing code has to be pleasant experience. Writing code is like painting or sculpting[雕塑；做(发式)；刻；塑(模型)]. You have to have a passion for it. You have to love it. Otherwise, what’s the point? You’d be better off working behind an assembly line for some car manufacturer.
My point is that you should use Go in place where you used to use C, Haskell, OCaml or whatever(I’m not a programming languages aficionado). But not where you’ve used Python, PHP, Ruby or Java.
Hence, after trying to bend Go to do my bidding, I am forfaiting and going back to PHP. I will most likely be using Go for some nano services where it has its place but absolutely not as my primary programming language.
I had a look at goroutines and it looks like I can do the same with Amp or ReactPHP if I’m on single threaded machine, which is the case of all cheaper cloud hosted computing options.
On multithreaded machine I can utilize the PThreads PHP extension and Amp/Parallel or potentially Swoole.
I have looked at other languages but I haven’t found any that would perform better than PHP and provide similar functionality at the same time. The exception being Java but I have quite a dislike for it. Don’t know why really, I’ve never wrote in it, but over the years I’ve read too much about being too resource-hungry and a lot of disliking in general.
Go’s big advantage is that it has really big standard library. It comes with very performant http server out of the box, which cannot be said about PHP. But I think that PHP profiled itself as being out-of-the-box solution whereas it’s ecosystem of libraries is really broad and I think it is time for me to have a good look at it, get my hands dirty and do some compilation stuff.
I feel that PHP is still going strong and is very mature, and now even performant, language but I think it is time for it to start taking parallelism and deployability more seriously. Hopefully, version 8 will bring some massive changes. I would love to see breaking the backward compatibility finally. It is the most limiting factor for progression in these large projects.
Some people have figured out what I just did before me: https://news.ycombinator.com/item?id=8201244
I guess, without trial and error we wouldn’t be able to progress anyway.
If you’re interested in concurrency or paralellism in PHP have a look at:
PS: I gave it another few days but it’s just painful to work with Go. It is a low level systems language and the lack of OOP is too much. My brain simply cannot comprehend procedural code / composition anymore. After over 4 years of day to day OOP, I cannot design procedural logic anymore. Lack of DRY drives me insane as well. The bytes/strings/json/http handling is also not pleasant to work with. The awesome positives of Go are simply not enough for me to compromise my productivity and joy of writing code.
If you google you can find that I’m not alone that feels like this, which quite surprised me, since I though I was simply going through the phase of learning something new(and the brain does not like that). But it looks that’s not the case.
It boils down to the “using the right tool” principle.
Go + nano services = yes,
Go + micro services = no. The end.
Sorry to read that you’re having so much trouble with Go. I can honestly feel the frustration in your blog post.
From a timing point of view, your experiences are in line with myself and friends who are using Go. Seems like you’ve gotten to the point where you’ve learned the basics and are now get into that zone where you can code more intuitively without having to stop and look things up all the time. Only now you’re bumping up against some of Go’s harder edges.
PHP is an extremely flexible language where you can approach problems from many different directions. Go tends to give you only one or two ways to approach a section of code. I could definitely see this as a point where you end up butting heads with Go.
If you haven’t completely given up on Go, the community would appreciate a follow-up post showing code examples where sections of code just aren’t working out for you. For example, some code blocks showing how you’re having trouble with interfaces. Also, some examples where you’re trying to work around inheritance.
Go doesn’t have generics. If they’re ever added to the language it probably won’t be for three or four years. Likely too late to do you any good and not something you could even count on being there. This does tend to make the language “wet” at times. However, for me at least, by the time code gets to business logic it’s usually boiled down to concrete types so I haven’t found as many issues with the business logic portion ballooning. Any chance we could see a few code chunks and your commentary where your business logic is getting out of hand?
In the end, Go may not be the language for you. It may not match your way of thinking. But it would be nice to understand your way of thinking and see some code examples of these higher level trouble spots. There might be ways to approach things that you haven’t considered. Even if these problems can’t be solved to your satisfaction it would help the Go community to see code examples of these problems and where you’re bumping up against them.
Hoping for a follow-up post.